Що є у вашому інструменті Mathematica? [зачинено]


152

Усі ми знаємо, що Mathematica - це чудово, але також часто не вистачає критичної функціональності. Які зовнішні пакети / інструменти / ресурси ви використовуєте з Mathematica?

Я відредагую (і запрошу будь-кого іншого зробити це) це головне повідомлення, щоб включити ресурси, орієнтовані на загальну придатність у науковому дослідженні і які якомога більше людей вважають корисними. Не соромтеся вносити будь-що, навіть невеликі фрагменти коду (як я це робив нижче для режиму часу).

Крім того, незадокументовані та корисні функції в Mathematica 7 та далі, які ви знайшли, або викопані з якогось паперу / сайту, є найбільш вітаючими.

Будь ласка, додайте короткий опис або коментар про те, чому щось чудово або яку корисну програму він пропонує. Якщо ви посилаєтесь на книги в Амазонії за допомогою партнерських посилань, будь ласка, зазначте це, наприклад, ввівши своє ім’я після посилання.


Пакети:

  1. LevelSchemeце пакет, який значно розширює можливості Mathematica для створення гарних сюжетів. Я використовую його, якщо не для чогось іншого, то для значно покращеного контролю над кліщами рам / осі. Його найновіша версія називається SciDraw, і вона вийде десь цього року.
  2. Девід Парк Presentation Package(50 доларів США - плата за оновлення не стягується)
  3. grassmannOpsПакет Джеремі Міхельсона надає ресурси для алгебри та обчислення зі змінними Grassmann та операторами, які мають нетривіальні комутаційні відносини.
  4. GrassmannAlgebraПакет та книга Джона Брауна для роботи з алгебрами Грассмана та Кліффорда.
  5. RISC (Науково-дослідний інститут символічних обчислень) має для завантаження різноманітні пакети для Mathematica (та інших мов). Зокрема, існує Theorema для автоматичного доведення теорем, і безліч пакетів для символічного підсумовування, різницевих рівнянь і т.д. На сторінці програмного забезпечення групи алгоритмічної комбінаторики в .

Інструменти:

  1. MASHце відмінний сценарій Perl Даніеля Ривза, який фактично забезпечує підтримку сценаріїв для Mathematica v7. (Зараз вбудований станом на Mathematica 8 з -scriptопцією.)
  2. З alternate Mathematica shellвходом для читання GNU (використовуючи лише python, * nix)
  3. Пакет ColourMaths дозволяє візуально вибирати частини виразу та маніпулювати ними. http://www.dbaileyconsultancy.co.uk/colour_maths/colour_maths.html

Ресурси:

  1. Власне сховище Wolfram MathSourceмає багато корисного, якщо вузькі зошити для різних програм. Також перегляньте інші розділи, такі як

  2. Mathematica Wikibook .

Книги:

  1. Програмування Mathematica: розширене вступ Леоніда Шифріна ( web, pdf) є обов'язковим для читання, якщо ви хочете зробити щось більше, ніж для циклів у Mathematica. Ми маємо задоволення від того, щоб Leonidвін сам відповідав на запитання.
  2. Квантові методи з математикою Джеймс Ф. Фейгін ( амазонка )
  3. Книга "Математика" Стівена Вольфрама ( амазонка ) ( web)
  4. Контур Шаума ( амазонка )
  5. Математика в дії Стен Вагон ( Amazon ) - 600 сторінок акуратних прикладів і підходить до версії 7. ​​Математика. Технології візуалізації особливо хороші, деякі з них можна побачити на авторських Demonstrations Page.
  6. Основи програмування математики Річарда Гейлорда ( pdf) - хороше стисле вступ до більшості того, що потрібно знати про програмування Mathematica.
  7. Математична кулінарна книга Сал Мангано, опублікована O'Reilly 2010, 832 сторінки. - Написано у відомому стилі кухонної книги O'Reilly: Проблема - рішення. Для проміжних продуктів.
  8. Диференціальні рівняння з Mathematica, 3-е вид. Elsevier 2004 Амстердам, Марта Л. Абелл, Джеймс П. Брезельтон - 893 сторінки Для початківців, навчіться розв’язувати DE та Mathematica одночасно.

Недокументовані (або малодокументовані) функції:

  1. Як налаштувати комбінації клавіш Mathematica. Див this question.
  2. Як перевірити закономірності та функції, які використовуються власними функціями Mathematica. Побачитиthis answer
  3. Як досягти послідовного розміру для GraphPlots в Mathematica? Див this question.
  4. Як виготовити документи та презентації за допомогою Mathematica. Див this question.

2
Mathematica 8 випускається зі значно кращою інтеграцією сценарію оболонки. wolfram.com/mathematica/new-in-8/mathematica-shell-scripts
Джошуа Мартелл

2
+1, для LevelScheme. Іноді це трохи повільно. Але, у нього є розумний метод створення галочок, і набагато простіше створити журнали, гідні макетів для графіки Grid, або щось подібне.
rcollyer

2
Як запропонував Олексій у коментарях до цього питання stackoverflow.com/questions/5152551/… , я запропонував перейменувати теги для Mathematica тут: meta.stackexchange.com/questions/81152/… . Будь-ласка, подивіться та підкажіть, якщо ви згодні. Я публікую його тут, тому що це питання має багато фаворитів у спільноті Mma тут.
Доктор Белісарій

1
Все, це питання справді має бути вікі спільноти з усіх звичних причин: на нього немає правильної відповіді, і це більше списку, ніж будь-що інше. Я вибачаюся перед усіма, хто неабияк нажився на репутації поза цим питанням.
rcollyer

2
Ці відповіді на це питання є конструктивними, і їх слід повторно відкрити.
MR

Відповіді:


29

Я вже говорив це раніше, але інструмент , який я знаходжу найбільш корисним є застосування Reapі Sowякий наслідує / розширює поведінку GatherBy:

SelectEquivalents[x_List,f_:Identity, g_:Identity, h_:(#2&)]:=
   Reap[Sow[g[#],{f[#]}]&/@x, _, h][[2]];

Це дозволяє мені групувати списки за будь-якими критеріями та трансформувати їх у процесі. Як це працює, це те, що функція критеріїв ( f) тегує кожен елемент у списку, кожен елемент потім перетворюється другою функцією, що постачається ( g), а конкретний вихід керується третьою функцією ( h). Функція hприймає два аргументи: тег та список зібраних елементів, які мають цей тег. Елементи зберігають свій початковий порядок, тому якщо ви встановите, h = #1&ви отримаєте несортовану Union, як у прикладах для Reap. Але, його можна використовувати для вторинної обробки.

Як приклад його корисності, я працював з Wannier90, який виводить просторово залежний гамільтоніан у файл, де кожен рядок є різним елементом у матриці, як описано нижче

rx ry rz i j Re[Hij] Im[Hij]

Щоб перетворити цей список у набір матриць, я зібрав усі підлісти, які містять однакову координату, перетворив інформацію про елемент у правило (тобто {i, j} -> Re [Hij] + I Im [Hij]), і потім перетворив зібрані правила в SparseArrayєдине вкладиш:

SelectEquivalents[hamlst, 
      #[[;; 3]] &, 
      #[[{4, 5}]] -> (Complex @@ #[[6 ;;]]) &, 
      {#1, SparseArray[#2]} &]

Чесно кажучи, це мій нож армії Швейцарії, і це робить складні речі дуже простими. Більшість моїх інших інструментів дещо залежать від домену, тому я, мабуть, не публікую їх. Однак більшість, якщо не всі, посилання на нихSelectEquivalents .

Редагувати : це не повністю імітує GatherByте, що не може просто згрупувати кілька рівнів виразу GatherBy. Однак,Map працює чудово для більшості того, що мені потрібно.

Приклад : @Ярослав Булатов попросив самостійний приклад. Ось один із моїх досліджень, який був значно спрощений. Отже, скажімо, у нас є множина точок у площині

In[1] := pts = {{-1, -1, 0}, {-1, 0, 0}, {-1, 1, 0}, {0, -1, 0}, {0, 0, 0}, 
 {0, 1, 0}, {1, -1, 0}, {1, 0, 0}, {1, 1, 0}}

і ми хотіли б зменшити кількість точок набором операцій симетрії. (Для допитливих ми генеруємо маленьку групу кожної точки.) Для цього прикладу давайте скористаємось чотирикратною віссю обертання навколо осі z

In[2] := rots = RotationTransform[#, {0, 0, 1}] & /@ (Pi/2 Range[0, 3]);

Використовуючи, SelectEquivalentsми можемо згрупувати точки, які створюють однаковий набір зображень у рамках цих операцій, тобто вони еквівалентні, використовуючи наступне

In[3] := SelectEquivalents[ pts, Union[Through[rots[#] ] ]& ] (*<-- Note Union*)
Out[3]:= {{{-1, -1, 0}, {-1, 1, 0}, {1, -1, 0}, {1, 1, 0}},
          {{-1, 0, 0}, {0, -1, 0}, {0, 1, 0}, {1, 0, 0}},
          {{0,0,0}}}

який створює 3 підсписки, що містять еквівалентні точки. (Зверніть увагу, Unionтут абсолютно життєво важливо, оскільки це забезпечує те, що однакове зображення створюється кожною точкою. Спочатку я використовував Sort, але якщо точка лежить на осі симетрії, вона є інваріантною при обертанні навколо цієї осі, що дає додаткове зображення самого себе . Отже, Unionвиключає ці зайві зображення. Також, GatherByце дало б такий самий результат.) У цьому випадку бали вже є такою формою, яку я буду використовувати, але мені потрібен лише репрезентативний пункт від кожного групування, і я хотів би порахувати рівнозначних балів Оскільки мені не потрібно перетворювати кожну точку, я використовуюIdentityфункція у другій позиції. До третьої функції нам потрібно бути обережними. Перший аргумент, переданий йому, - це зображення точок під обертаннями, які для точки {0,0,0}є переліком чотирьох однакових елементів, і використовуючи його, викинете рахунок. Однак другий аргумент - це лише перелік усіх елементів, які мають цей тег, тому він буде містити лише {0,0,0}. У коді,

In[4] := SelectEquivalents[pts,  
             Union[Through[rots[#]]]&, #&, {#2[[1]], Length[#2]}& ]
Out[4]:= {{{-1, -1, 0}, 4}, {{-1, 0, 0}, 4}, {{0, 0, 0}, 1}}

Зауважте, цей останній крок може бути виконаний так само легко

In[5] := {#[[1]], Length[#]}& /@ Out[3]

Але з цього та менш повного прикладу вище можна легко зрозуміти, як можливі дуже складні перетворення з мінімальним кодом.


Оригінальний код Fortran77 був реструктурований в День Подяки 1996 року, а отже, протягом багатьох років відомий як turkey.f ...: D Дуже гарна графіка BTW. Згадав мене чудовисько Фалікова ...
Доктор Белісарій

@belisarius, я не читав історії, це смішно. Я тільки почав використовувати Wannier90, але це один із найкраще організованих і добре написаних Fortranкодів, які я бачив. Змушує мене майже розглянути можливість використання Fortran...
rcollyer

Цікаво, чи можете ви додати автономний приклад SelectEquivalents у дії
Ярослав Булатов

@ Ярослав Булатов додав приклад на запит. Повідомте мене, якщо це допомагає. Якщо цього не відбудеться, ми побачимо, що ми можемо зробити.
rcollyer

Ви отримуєте галочку на це "запитання" для найцікавішого вкладу фрагменту коду.
Тимо

57

Однією з приємних речей інтерфейсу ноутбука Mathematica є те, що він може оцінювати вирази будь-якою мовою, не лише Mathematica. В якості простого прикладу розглянемо створення нового типу вхідної комірки Shell, яка передає міститься вираз оболонці операційної системи для оцінки.

Спочатку визначте функцію, яка делегує оцінку текстової команди зовнішній оболонці:

shellEvaluate[cmd_, _] := Import["!"~~cmd, "Text"]

Другий аргумент потрібен і ігнорується з причин, які стануть очевидними пізніше. Далі ми хочемо створити новий стиль під назвою Shell :

  1. Відкрийте новий зошит.
  2. Виберіть пункт меню Формат / Редагувати таблицю стилів ...
  3. У діалоговому вікні біля Введіть назву стилю: введіть Shell.
  4. Виберіть дужку комірок поруч із новим стилем.
  5. Виберіть пункт меню Cell / Show Expression
  6. Перепишіть вираз комірки за допомогою тексту кроку 6, наведеного нижче.
  7. Ще раз виберіть пункт меню Cell / Show Expression
  8. Закрийте діалогове вікно.

Використовуйте такий вираз клітинки як текст кроку 6 :

Cell[StyleData["Shell"],
 CellFrame->{{0, 0}, {0.5, 0.5}},
 CellMargins->{{66, 4}, {0, 8}},
 Evaluatable->True,
 StripStyleOnPaste->True,
 CellEvaluationFunction->shellEvaluate,
 CellFrameLabels->{{None, "Shell"}, {None, None}},
 Hyphenation->False,
 AutoQuoteCharacters->{},
 PasteAutoQuoteCharacters->{},
 LanguageCategory->"Formula",
 ScriptLevel->1,
 MenuSortingValue->1800,
 FontFamily->"Courier"]

Більшість цього виразу було скопійовано безпосередньо із вбудованого стилю програми . Ключові зміни - це рядки:

 Evaluatable->True,
 CellEvaluationFunction->shellEvaluate,
 CellFrameLabels->{{None, "Shell"}, {None, None}},

Evaluatableдозволяє функцію SHIFT + ENTER для комірки. Оцінка буде називати CellEvaluationFunctionпередачу вмісту комірки та типу вмісту як аргументи ( shellEvaluateігнорує останній аргумент). CellFrameLabelsце лише вибагливість, що давайте користувачеві визначити, що ця клітина незвичайна.

Маючи все це на місці, тепер ми можемо ввести та оцінити вираз оболонки:

  1. У зошиті, створеному на кроках вище, створіть порожню клітинку та виберіть дужку комірок.
  2. Виберіть пункт меню Формат / Стиль / Оболонка .
  3. Введіть дійсну команду оболонки операційної системи в комірку (наприклад, 'ls' в Unix або 'dir' в Windows).
  4. Натисніть SHIFT + ENTER.

Найкраще зберігати цей визначений стиль у центрально розташованому аркуші стилів. Крім того, функції оцінювання, як-от shellEvaluate, найкраще визначаються як заглушки, використовуючи DeclarePackage вinit.m . Деталі обох цих заходів виходять за рамки цієї відповіді.

Завдяки цій функціональності можна створити зошити, які містять вхідні вирази в будь-якому синтаксисі, що цікавить. Функція оцінювання може бути записана чистою Mathematica або делегувати будь-яку або всі частини оцінки зовнішньому агентству. Майте в виду , що існують і інші гачки , які відносяться до клітки оцінки, як CellEpilog, CellPrologі CellDynamicExpression.

Загальна модель включає в себе написання вхідного тексту виразу у тимчасовий файл, компіляцію файлу якоюсь мовою, запуск програми та фіксацію виводу для остаточного відображення у вихідній комірці. Є багато деталей, на які слід звернути увагу при впровадженні повного рішення такого типу (наприклад, правильне захоплення повідомлень про помилки), але треба цінувати той факт, що робити такі речі не тільки можна, але й практично.

Що стосується особистої ноти, саме такі функції роблять інтерфейс ноутбука центром мого всесвіту програмування.

Оновлення

Наступна допоміжна функція корисна для створення таких комірок:

evaluatableCell[label_String, evaluationFunction_] :=
  ( CellPrint[
      TextCell[
        ""
      , "Program"
      , Evaluatable -> True
      , CellEvaluationFunction -> (evaluationFunction[#]&)
      , CellFrameLabels -> {{None, label}, {None, None}}
      , CellGroupingRules -> "InputGrouping"
      ]
    ]
  ; SelectionMove[EvaluationNotebook[], All, EvaluationCell]
  ; NotebookDelete[]
  ; SelectionMove[EvaluationNotebook[], Next, CellContents]
  )

Він використовується таким чином:

shellCell[] := evaluatableCell["shell", Import["!"~~#, "Text"] &]

Тепер, якщо shellCell[]буде оцінено, вхідна комірка буде видалена і замінена новою вхідною коміркою, яка оцінює її вміст як команду оболонки.


3
@WReach +100! Я б хотів, щоб я знав це раніше! Це дуже корисні речі, принаймні для мене. Дякую, що поділились!
Леонід Шифрін

Це виглядає досить пишно! CellEvaluationFunctionможна також використовувати для злому синтаксису низького рівня.
Mr.Wizard

@Leonid Принаймні для FrontEnd - CellEvaluationFunctionце гачок, який ви шукали?
Mr.Wizard

2
Крім того: є ще один Cellваріант, який пов'язаний з оцінкою клітин - Evaluator -> "EvaluatorName". Значення "EvaluatorName"можна налаштувати за допомогою діалогового вікна Оцінка :: Конфігурація ядра ... Я досі не знаю, чи можливо це налаштувати програмно програмою ... Ця методика дозволяє використовувати різні MathKernel в різних Cells в одному ноутбуці. Ці MathKernel можуть бути з різних встановлених версій Mathematica .
Олексій Попков

1
@Szabolcs Всі мої власні використання цієї методики включають або stdin _ / _ stdout підхід, як показано вище, або автономний віддалений запит, такий як SQL-запит або операція HTTP. Ви можете спробувати налаштувати веб-додаток Python REPL (наприклад, цей ) та взаємодіяти з ним за допомогою Importабо, можливо, запустити зовнішній процес Python та спілкуватися через його потоки (наприклад, за допомогою Java ProcessBuilder ). Я впевнений, що є кращий спосіб Mathematica - це звучить як хороший питання :)
WReach

36

Тодд Гейлі (Wolfram Research) просто надсилає мені гарний хак, який дозволяє "обернути" вбудовані функції з довільним кодом. Я відчуваю, що мені потрібно поділитися цим корисним інструментом. Далі відповідь Тодда на мій question.

Трохи цікавої (?) Історії: Цей стиль злому для "обгортання" вбудованої функції був винайдений близько 1994 року Робі Віллегасом, і я, як не дивно, для функції Message, в пакеті під назвою ErrorHelp, який я написав для журналу Mathematica тоді. З тих пір воно було використано багатьма людьми. Це трохи хитрість інсайдера, але я вважаю, що справедливо сказати, що це стало канонічним способом введення власного коду у визначення вбудованої функції. Це добре працює. Звичайно, ви можете помістити змінну $ inMsg у будь-який приватний контекст.

Unprotect[Message];

Message[args___] := Block[{$inMsg = True, result},
   "some code here";
   result = Message[args];
   "some code here";
   result] /; ! TrueQ[$inMsg]

Protect[Message];

@Alexey У мене виникають труднощі з розумінням цього. Чи можете ви пояснити, як це працює? Чи не повинен десь бути Захист [Повідомлення]? І чи не містить цей приклад нескінченна рекурсія? І,! TrueQ [$ inMsg] чи має це сенс з $ inMsg, визначеним у межах блоку блоку та невизначеним поза Блоком?
Sjoerd C. de Vries

9
@Sjoerd З того, що я розумію, Unprotectсправді має бути, це було просто осторонь. Суть Block(динамічне оцінювання) і $inMsgполягає саме у запобіганні нескінченної рекурсії. Оскільки $inMsgзовні не визначено (це важлива вимога), спочатку TrueQоцінюємо False, і ми входимо в тіло функції. Але коли у нас виклик функції всередині тіла, умова оцінюється на False(оскільки змінна була переосмислена Блоком). Таким чином, визначене користувачем правило не відповідає, а замість цього використовується вбудоване правило.
Леонід Шифрін

1
@Leonid Дякую, зараз я розумію. Дуже розумний!
Шьорд К. де Вріс

1
Щойно я виявив, що цю техніку обговорював Робі Віллегас з Wolfram Research на Конференції розробників 1999 року. Див. Зошит "Робота з неоціненими виразами", розміщений тут . У цьому зошиті Робі Віллегас обговорює цей трюк у підрозділі "Мій блок-трюк для захоплення дзвінків до вбудованих функцій".
Олексій Попков

1
@ Mr.Wizard Це не єдиний спосіб зробити це. Тривалий час я використовував версію, де ви переосмислюєте час DownValuesвиконання, ви можете подивитися цей пост groups.google.com/group/comp.soft-sys.math.mathematica/… , наприклад ( SetDelayedперезначення) . Але мій метод менш елегантний, менш надійний, більш схильний до помилок і робить обриви від рекурсії набагато менш тривіальними для реалізації. Тож у більшості ситуацій метод, описаний @Alexey, перемагає.
Леонід Шифрін

25

Це не повний ресурс, тому я кидаю його тут, у розділі відповідей, але я вважаю це дуже корисним під час з'ясування питань щодо швидкості (що, на жаль, є значною частиною того, що стосується програмування Mathematica).

timeAvg[func_] := Module[
{x = 0, y = 0, timeLimit = 0.1, p, q, iterTimes = Power[10, Range[0, 10]]},
Catch[
 If[(x = First[Timing[(y++; Do[func, {#}]);]]) > timeLimit,
    Throw[{x, y}]
    ] & /@ iterTimes
 ] /. {p_, q_} :> p/iterTimes[[q]]
];
Attributes[timeAvg] = {HoldAll};

Використання тоді просто timeAvg@funcYouWantToTest.

EDIT: Містер Чарівник запропонував більш просту версію, яка усуває Throwі Catchтрохи простіше розбирає:

SetAttributes[timeAvg, HoldFirst]
timeAvg[func_] := Do[If[# > 0.3, Return[#/5^i]] & @@ 
                     Timing @ Do[func, {5^i}]
                     ,{i, 0, 15}]

EDIT: Ось версія з acl (взята звідси ):

timeIt::usage = "timeIt[expr] gives the time taken to execute expr, \
  repeating as many times as necessary to achieve a total time of 1s";

SetAttributes[timeIt, HoldAll]
timeIt[expr_] := Module[{t = Timing[expr;][[1]], tries = 1},
  While[t < 1., tries *= 2; t = Timing[Do[expr, {tries}];][[1]];]; 
  t/tries]

Зробив це ще раз і знову ... час ввести у власний мішок. tnx!
Доктор Белісарій

1
Одна з проблем цього коду (ну може бути, це точка зору перфекціоніста) полягає в тому, що ми можемо зловити щось, що ми не кинули, і інтерпретувати це як неправильний результат часу. Обидва Catchі Throwповинні були використовуватися з унікальними тегами виключень.
Леонід Шифрін

2
Тімо, я радий, що тобі подобається моя вистава, щоб включити її. Дякую, що ви також дали мені кредит. Мені цікаво, як ви переформатували мій код. Я не дотримуюся жодних конкретних вказівок у власному коді, крім того, щоб полегшити читання самостійно; чи за вашим переформатуванням стоїть школа думки, чи це просто перевага? Mathematica не заохочує точне форматування коду через те, як він поповнює введення, але розміщення коду тут змушує мене почати думати про це. До речі, я думаю, ви маєте на увазі " Throwі Catch", а не " Reapі Sow".
Mr.Wizard

1
@Simon, Mr.Wizard, я використовую цей метод, щоб відрізняти за часом версії дрібних функцій, які називатимуться багато разів. Не обов'язково в структурі циклу, але, безумовно, в конструкціях, які оптимізує MMA. У цьому контексті час виконання циклу має сенс, і продуктивність буде наближена до програми реального життя. Для тимчасових великих складних функцій (можливо, навіть цілих комірок ініціалізації) метод Саймона дасть кращий результат. Загалом, хоча мене більше цікавлять відносні значення і будь-який метод повинен працювати там.
Тимо,

3
Зараз це RepeatedTimingпотрібно зробити.
masterxilo

20

Internal`InheritedBlock

Нещодавно я дізнався про існування такої корисної функції, як Internal`InheritedBlockз цього повідомлення Даніеля Ліхтблау в офіційній групі новин.

Як я розумію, Internal`InheritedBlockдозволяє передавати копію вихідної функції всередині Blockобласті:

In[1]:= Internal`InheritedBlock[{Message},
Print[Attributes[Message]];
Unprotect[Message];
Message[x___]:=Print[{{x},Stack[]}];
Sin[1,1]
]
Sin[1,1]
During evaluation of In[1]:= {HoldFirst,Protected}
During evaluation of In[1]:= {{Sin::argx,Sin,2},{Internal`InheritedBlock,CompoundExpression,Sin,Print,List}}
Out[1]= Sin[1,1]
During evaluation of In[1]:= Sin::argx: Sin called with 2 arguments; 1 argument is expected. >>
Out[2]= Sin[1,1]

Я думаю, що ця функція може бути дуже корисною для всіх, кому потрібно тимчасово змінювати вбудовані функції!

Порівняння з Блоком

Давайте визначимо деяку функцію:

a := Print[b]

Тепер ми хочемо передати копію цієї функції в Blockобласть застосування. Наївна проба не дає того, чого ми хочемо:

In[2]:= Block[{a = a}, OwnValues[a]]

During evaluation of In[9]:= b

Out[2]= {HoldPattern[a] :> Null}

Зараз намагаємось використовувати відкладене визначення у першому аргументі Block(це також недокументована функція):

In[3]:= Block[{a := a}, OwnValues[a]]
Block[{a := a}, a]

Out[3]= {HoldPattern[a] :> a}

During evaluation of In[3]:= b

Ми бачимо, що в цьому випадку aпрацює, але ми не отримали копії оригіналу aвсерединіBlock області.

Тепер спробуємо Internal`InheritedBlock:

In[5]:= Internal`InheritedBlock[{a}, OwnValues[a]]

Out[5]= {HoldPattern[a] :> Print[b]}

У нас є копія оригінального визначення aвсередині Blockобласті, і ми можемо модифікувати його так, як ми хочемо, не впливаючи на глобальне визначення для a!


+1 Дуже зручно! Ще один інструмент в сумці, і на 10 очок ближче до Права редагування для вас.
Mr.Wizard

Для мене це виглядає як варіант раннього чи пізнього або відсутність і повне оцінювання.
користувач2432923

19

Mathematica - це гострий інструмент, але він може вирізати вас своєю дещо нетиповою поведінкою та лавинами криптовалютних діагностичних повідомлень . Один із способів вирішити це - визначити функції, що відповідають цій ідіомі:

ClearAll@zot
SetAttributes[zot, ...]
zot[a_] := ...
zot[b_ /; ...] := ...
zot[___] := (Message[zot::invalidArguments]; Abort[])

Це дуже багато котлів, які я часто спокушаюсь пропустити. Особливо при прототипуванні, що трапляється багато в Mathematica. Отже, я використовую макрос під назвоюdefine що дозволяє мені бути дисциплінованим, із значно меншою кількістю котлів.

Основне використання defineтаке:

define[
  fact[0] = 1
; fact[n_ /; n > 0] := n * fact[n-1]
]

fact[5]

120

Спочатку це не дуже схоже, але є деякі приховані переваги. Перша послуга, яка defineнадається, - це те, що вона автоматично застосовуєтьсяClearAll до визначеного символу. Це забезпечує відсутність визначень залишків - поширене явище під час початкового розвитку функції.

Друга послуга полягає в тому, що визначена функція автоматично «закривається». Під цим я маю на увазі, що функція видасть повідомлення і скасує, якщо на нього буде викликано список аргументів, який не відповідає одному з визначень:

fact[-1]

define::badargs: There is no definition for 'fact' applicable to fact[-1].
$Aborted

Це первинне значення define , яке виявляє дуже поширений клас помилок.

Ще однією зручністю є стислий спосіб вказати атрибути функції, що визначається. Зробимо функцію Listable:

define[
  fact[0] = 1
; fact[n_ /; n > 0] := n * fact[n-1]
, Listable
]

fact[{3, 5, 8}]

{6, 120, 40320}

Окрім усіх звичайних атрибутів, defineприймає додатковий атрибут, який називається Open. Це запобігає defineдоданню визначення функції помилок у програму:

define[
  successor[x_ /; x > 0] := x + 1
, Open
]

successor /@ {1, "hi"}

{2, successor["hi"]}

Для функції може бути визначено кілька атрибутів:

define[
  flatHold[x___] := Hold[x]
, {Flat, HoldAll}
]

flatHold[flatHold[1+1, flatHold[2+3]], 4+5]

Hold[1 + 1, 2 + 3, 4 + 5]

Без подальшої приналежності, ось визначення define:

ClearAll@define
SetAttributes[define, HoldAll]
define[body_, attribute_Symbol] := define[body, {attribute}]
define[body:(_Set|_SetDelayed), attributes_List:{}] := define[CompoundExpression[body], attributes]
define[body:CompoundExpression[((Set|SetDelayed)[name_Symbol[___], _])..], attributes_List:{}] :=
  ( ClearAll@name
  ; SetAttributes[name, DeleteCases[attributes, Open]]
  ; If[!MemberQ[attributes, Open]
    , def:name[___] := (Message[define::badargs, name, Defer@def]; Abort[])
    ]
  ; body
  ;
  )
def:define[___] := (Message[define::malformed, Defer@def]; Abort[])

define::badargs = "There is no definition for '``' applicable to ``.";
define::malformed = "Malformed definition: ``";

Викладена реалізація не підтримує ні збільшення значень, ні currying, ні шаблони, більш загальні, ніж просте визначення функції. Однак це залишається корисним.


2
+1 - це дійсно корисні речі. Я використовував подібні інструменти. Макроси (а також інтроспекція та інші методи метапрограмування) можуть бути дуже потужними, але, здається, недооцінені загалом у спільноті Mathematica, або, принаймні, це було моє враження досі.
Леонід Шифрін

Я щойно визначив щось подібне. +1 для підтримки CompoundExpression для виконання декількох визначень, Abort [] (здається, краще, ніж більше повідомлень) та Open (приємно, наприклад, для конструкторів).
masterxilo

16

Почніть без відкритого порожнього зошита

Мене турбувало те, що Mathematica почав з відкритого пустого ноутбука. Я міг би закрити цей зошит зі сценарієм, але він все одно коротко спалахне. Мій хак - це створити файл, Invisible.nbщо містить:

Notebook[{},Visible->False]

І додайте це до мого Kernel\init.m:

If[Length[Notebooks["Invisible*"]] > 0, 
  NotebookClose[Notebooks["Invisible*"][[1]]]
]

SetOptions[$FrontEnd,
  Options[$FrontEnd, NotebooksMenu] /. 
    HoldPattern["Invisible.nb" -> {__}] :> Sequence[]
]

Зараз я починаю Mathematica, відкриваючи Invisible.nb

Може бути і кращий спосіб, але це мені добре послужило.


Індивідуальні FoldтаFoldList

Fold[f, x] робиться рівнозначним Fold[f, First@x, Rest@x]

До речі, я вважаю, що це може знайти свій шлях у майбутній версії Mathematica.

Сюрприз! Це було впроваджено, хоча воно наразі не є документальним. Мені інформують, що це було здійснено в 2011 році Олівером Рубенконігом, мабуть, недовго після того, як я опублікував це. Дякую, Олівер Рюбенконіг!

Unprotect[Fold, FoldList]

Fold[f_, h_[a_, b__]] := Fold[f, Unevaluated @ a, h @ b]
FoldList[f_, h_[a_, b__]] := FoldList[f, Unevaluated @ a, h @ b]

(* Faysal's recommendation to modify SyntaxInformation *)
SyntaxInformation[Fold]     = {"ArgumentsPattern" -> {_, _, _.}};
SyntaxInformation[FoldList] = {"ArgumentsPattern" -> {_, _., {__}}};

Protect[Fold, FoldList]

Оновлено, щоб дозволити це:

SetAttributes[f, HoldAll]
Fold[f, Hold[1 + 1, 2/2, 3^3]]
f[f[1 + 1, 2/2], 3^3]

"Динамічний розділ"

Дивіться сторінку Mathematica.SE №7512 для нової версії цієї функції.

Часто мені хочеться розділити список відповідно до послідовності довжин.

Приклад псевдокоду:

partition[{1,2,3,4,5,6}, {2,3,1}]

Вихід: {{1,2}, {3,4,5}, {6}}

Я придумав це:

dynP[l_, p_] := 
 MapThread[l[[# ;; #2]] &, {{0} ~Join~ Most@# + 1, #} &@Accumulate@p]

Що я потім завершив цим, включаючи тестування аргументів:

dynamicPartition[l_List, p : {_Integer?NonNegative ..}] :=
  dynP[l, p] /; Length@l >= Tr@p

dynamicPartition[l_List, p : {_Integer?NonNegative ..}, All] :=
  dynP[l, p] ~Append~ Drop[l, Tr@p] /; Length@l >= Tr@p

dynamicPartition[l_List, p : {_Integer?NonNegative ..}, n__ | {n__}] :=
  dynP[l, p] ~Join~ Partition[l ~Drop~ Tr@p, n] /; Length@l >= Tr@p

Третій аргумент контролює те, що відбувається з елементами, що не відповідають специфікації спліт.


Прийоми математики Сабольча

Я використовую найчастіше - це вставити табличну палітру даних

CreatePalette@
 Column@{Button["TSV", 
    Module[{data, strip}, 
     data = NotebookGet[ClipboardNotebook[]][[1, 1, 1]];
     strip[s_String] := 
      StringReplace[s, RegularExpression["^\\s*(.*?)\\s*$"] -> "$1"];
     strip[e_] := e;
     If[Head[data] === String, 
      NotebookWrite[InputNotebook[], 
       ToBoxes@Map[strip, ImportString[data, "TSV"], {2}]]]]], 
   Button["CSV", 
    Module[{data, strip}, 
     data = NotebookGet[ClipboardNotebook[]][[1, 1, 1]];
     strip[s_String] := 
      StringReplace[s, RegularExpression["^\\s*(.*?)\\s*$"] -> "$1"];
     strip[e_] := e;
     If[Head[data] === String, 
      NotebookWrite[InputNotebook[], 
       ToBoxes@Map[strip, ImportString[data, "CSV"], {2}]]]]], 
   Button["Table", 
    Module[{data}, data = NotebookGet[ClipboardNotebook[]][[1, 1, 1]];
     If[Head[data] === String, 
      NotebookWrite[InputNotebook[], 
       ToBoxes@ImportString[data, "Table"]]]]]}

Змінення зовнішніх даних зсередини Compile

Нещодавно Даніель Ліхтлау показав цей метод, якого я ніколи не бачив. На мій погляд, це значно розширює корисністьCompile

ll = {2., 3., 4.};
c = Compile[{{x}, {y}}, ll[[1]] = x; y];

c[4.5, 5.6]

ll

(* Out[1] = 5.6  *)

(* Out[2] = {4.5, 3., 4.}  *)

3
+1 Гарна колекція! Щодо зовнішніх модифікацій зсередини Compile- весь мій пост тут: stackoverflow.com/questions/5246330/… , повинен був продемонструвати цю можливість у нетривіальній обстановці (там було розміщено коротше та швидше рішення проблеми, про яку йдеться, вже розміщена там) . IMO, найбільша виграш тут - це можливість емуляції проходження посилання та розбиття великих компільованих функцій на більш керовані та багаторазові блоки.
Леонід Шифрін

1
Ви можете також налаштувати інформацію синтаксису Fold і FoldList в новому визначенні: SyntaxInformation [Fold] = { "ArgumentsPattern" -> {_, . , _}}; SyntaxInformation [FoldList] = {"АргументиПаттерн" -> {_, _., {_ }}};
faysou

14

Загальні проблеми експорту в PDF / EMF

1) Це абсолютно несподівано і без документації, але Mathematica експортує та зберігає графіку у форматах PDF та EPS, використовуючи набір визначень стилів, що відрізняється від того, яке використовується для відображення ноутбуків на екрані. За замовчуванням Ноутбуки відображаються на екрані в середовищі стилю "Робочий" (що є типовим значенням для ScreenStyleEvironmentглобальної $FrontEndопції), але друкуються в "Printout"середовищі стилів (що є типовим значенням для PrintingStyleEnvironmentглобального $FrontEndпараметра). Коли експортується графіка у растрових форматах, таких як GIF та PNG або у форматі EMF, середовище стилю Mathematica генерує графіку, яка виглядає так, як виглядає всередині Notebook. Здається, що"Working" використовується для візуалізації в цьому випадку. Але це не так, коли ви експортуєте / зберігаєте що-небудь у форматах PDF або EPS! У цьому випадку "Printout"середовище стилю використовується за замовчуваннямщо дуже глибоко відрізняється від середовища "робочого" стилю. Перш за все, в "Printout"стилі набори навколишнього середовища Magnificationдо 80% . По-друге, він використовує власні значення для розмірів шрифту різних стилів, і це призводить до непослідовних змін розміру шрифту в генерованому PDF-файлі порівняно з оригінальним екранним поданням. Останнє можна назвати коливаннями FontSize, які дуже дратують. Але щасливо цього можна уникнути , встановивши PrintingStyleEnvironmentглобальну $FrontEndопцію "Робота" :

SetOptions[$FrontEnd, PrintingStyleEnvironment -> "Working"]

2) Поширена проблема з експортом у формат ЕРС полягає в тому, що більшість програм (не тільки Mathematica ) генерують файл, який виглядає добре за розміром за замовчуванням, але стає некрасивим, коли ви збільшуєте його. Це тому, що метафайли відбираються у вибірку при вірності роздільної здатності екрана . Якість сформованого файлу ЕРС можна підвищити, Magnifyвикористовуючи оригінальний графічний об'єкт, завдяки чому точність вибірки оригінальної графіки стає набагато точнішою. Порівняйте два файли:

graphics1 = 
  First@ImportString[
    ExportString[Style["a", FontFamily -> "Times"], "PDF"], "PDF"];
graphics2 = Magnify[graphics1, 10];
Export["C:\\test1.emf", graphics1]
Export["C:\\test2.emf", graphics2]

Якщо ви вставите ці файли в Microsoft Word і збільшите їх, ви побачите, що перший "a" має на ньому пильний зуб, а другий - (протестовано з Mathematica 6).

Кріс ДегненImageResolution запропонував інший шлях (цей варіант має ефект, принаймні, починаючи з Mathematica 8):

Export["C:\\test1.emf", graphics1]
Export["C:\\test2.emf", graphics1, ImageResolution -> 300]

3) У Mathematica є три способи перетворення графіки в метафайл: через Exportдо "EMF"(настійно рекомендується чином: виробляє метафайл з максимально можливою якістю), з допомогою Save selection As...пункту меню ( виробляє набагато менше точну цифру , не рекомендується) і з допомогою Edit ► Copy As ► Metafileпункту меню ( я настійно рекомендую проти цього маршруту ).


13

За популярним попитом, код для створення топ-10 сюжетів відповіді SO (крім анотацій ) за допомогою API SO .

введіть тут опис зображення

getRepChanges[userID_Integer] :=
 Module[{totalChanges},
  totalChanges = 
   "total" /. 
    Import["http://api.stackoverflow.com/1.1/users/" <> 
      ToString[userID] <> "/reputation?fromdate=0&pagesize=10&page=1",
      "JSON"];
  Join @@ Table[
    "rep_changes" /. 
     Import["http://api.stackoverflow.com/1.1/users/" <> 
       ToString[userID] <> 
       "/reputation?fromdate=0&pagesize=10&page=" <> ToString[page], 
      "JSON"],
    {page, 1, Ceiling[totalChanges/10]}
    ]
  ]

topAnswerers = ({"display_name", 
      "user_id"} /. #) & /@ ("user" /. ("top_users" /. 
      Import["http://api.stackoverflow.com/1.1/tags/mathematica/top-\
answerers/all-time", "JSON"]))

repChangesTopUsers =
  Monitor[Table[
    repChange = 
     ReleaseHold[(Hold[{DateList[
              "on_date" + AbsoluteTime["January 1, 1970"]], 
             "positive_rep" - "negative_rep"}] /. #) & /@ 
        getRepChanges[userID]] // Sort;
    accRepChange = {repChange[[All, 1]], 
       Accumulate[repChange[[All, 2]]]}\[Transpose],
    {userID, topAnswerers[[All, 2]]}
    ], userID];

pl = DateListLogPlot[
  Tooltip @@@ 
   Take[({repChangesTopUsers, topAnswerers[[All, 1]]}\[Transpose]), 
    10], Joined -> True, Mesh -> None, ImageSize -> 1000, 
  PlotRange -> {All, {10, All}}, 
  BaseStyle -> {FontFamily -> "Arial-Bold", FontSize -> 16}, 
  DateTicksFormat -> {"MonthNameShort", " ", "Year"}, 
  GridLines -> {True, None}, 
  FrameLabel -> (Style[#, FontSize -> 18] & /@ {"Date", "Reputation", 
      "Top-10 answerers", ""})]

1
Бретт опублікував питання, задавши майже цей точний код. Можливо, це найдоречніше там, з підкруткою чи двома, щоб відповідати питанню. Я б насправді був гідним представника, на відміну від цього питання.
rcollyer

@rcollyer має рацію. Це "Community Wiki"
доктор belisarius

@belisarius Я щойно скопіював це у відповідь на запитання Бретта ...
Sjoerd C. de Vries

@Sjoerd Ваш сюжет не оновлюється автоматично.
Доктор Белісарій

@belisarius Насправді я сподівався, що ти візьмеш на себе це завдання ... ;-)
Sjoerd C. de Vries

13

Кешування виразів

Я вважаю ці функції дуже корисними для кешування будь-якого виразу. Цікавим для цих двох функцій є те, що сам утримуваний вираз використовується як ключ кеш-пам'яті / символу Cache або CacheIndex, порівняно з відомою пам’яткою математики, де ви можете кешувати результат лише в тому випадку, якщо функція визначена як f [x_]: = f [x] = ... Отже, ви можете кешувати будь-яку частину коду, це корисно, якщо функцію потрібно викликати кілька разів, але лише деякі частини коду не повинні бути перераховані.

Кешувати вираз незалежно від його аргументів.

SetAttributes[Cache, HoldFirst];
c:Cache[expr_] := c = expr;

Ex: Cache[Pause[5]; 6]
Cache[Pause[5]; 6]

Вдруге вираз повертає 6, не чекаючи.

Кешувати вираз, використовуючи псевдонім вираз, який може залежати від аргументу кешованого виразу.

SetAttributes[CacheIndex, HoldRest];
c:CacheIndex[index_,expr_] := c = expr;

Ex: CacheIndex[{"f",2},x=2;y=4;x+y]

Якщо для expr потрібен деякий час для обчислення, набагато швидше оцінити {"f", 2}, наприклад, для отримання кешованого результату.

Для варіації цих функцій для того, щоб мати локалізований кеш (тобто пам'ять кешу автоматично звільняється поза блокової конструкції), дивіться цей пост. Уникайте повторних дзвінків на інтерполяцію

Видалення кешованих значень

Щоб видалити кешовані значення, коли вам невідомо кількість визначень функції. Я вважаю, що у дефініціях дефініція є дефісом.

DeleteCachedValues[f_] := 
       DownValues[f] = Select[DownValues[f], !FreeQ[Hold@#,Pattern]&];

Видалити кешовані значення, коли ви знаєте кількість визначень функції (йде трохи швидше).

DeleteCachedValues[f_,nrules_] := 
       DownValues[f] = Extract[DownValues[f], List /@ Range[-nrules, -1]];

Для цього використовується той факт, що визначення функції знаходяться в кінці списку DownValues, кешовані значення знаходяться раніше.

Використання символів для зберігання даних та об'єктоподібних функцій

Також тут є цікаві функції для використання символів, як об'єкти.

Вже відомо, що ви можете зберігати дані в символах та швидко отримувати доступ до них за допомогою DownValues

mysymbol["property"]=2;

Ви можете отримати доступ до списку ключів (або властивостей) символу, скориставшись цими функціями, залежно від того, які типи дерева надсилаються у публікації на цьому веб-сайті:

SetAttributes[RemoveHead, {HoldAll}];
RemoveHead[h_[args___]] := {args};
NKeys[symbol_] := RemoveHead @@@ DownValues[symbol(*,Sort->False*)][[All,1]];
Keys[symbol_] := NKeys[symbol] /. {x_} :> x;

Я дуже часто використовую цю функцію для відображення всіх відомостей, що містяться в DownValues ​​символу:

PrintSymbol[symbol_] :=
  Module[{symbolKeys},
    symbolKeys = Keys[symbol];
    TableForm@Transpose[{symbolKeys, symbol /@ symbolKeys}]
  ];

Нарешті, ось простий спосіб створити символ, який поводиться як об’єкт у об'єктно-орієнтованому програмуванні (він просто відтворює найосновнішу поведінку OOP, але я вважаю синтаксис елегантним):

Options[NewObject]={y->2};
NewObject[OptionsPattern[]]:=
  Module[{newObject},
    newObject["y"]=OptionValue[y];

    function[newObject,x_] ^:= newObject["y"]+x;
    newObject /: newObject.function2[x_] := 2 newObject["y"]+x;

    newObject
  ];

Властивості зберігаються як DownValues, а методи - як затримані оновлення в символі, створеному модулем, який повертається. Я знайшов синтаксис function2, який є звичайним синтаксисом OO для функцій у структурі даних дерева в Mathematica .

Перелік існуючих типів значень, які має кожен символ, див. У розділі http://reference.wolfram.com/mathematica/tutorial/PatternsAndTransformationRules.html та http://www.verbeia.com/mathematica/tips/HTMLLinks/Tricks_Misc_4.html .

Наприклад, спробуйте це

x = NewObject[y -> 3];
function[x, 4]
x.function2[5]

Ви можете піти далі, якщо хочете наслідувати спадкування об'єктів, використовуючи пакет під назвою InheritRules, доступний тут http://library.wolfram.com/infocenter/MathSource/671/

Ви також можете зберігати визначення функції не в newObject, а в символі типу, тож якщо NewObject повернув тип [newObject] замість newObject, ви могли б визначити функцію та function2, подібні цій за межами NewObject (а не всередині) і мати таке ж використання, як раніше .

function[type[object_], x_] ^:= object["y"] + x;
type /: type[object_].function2[x_] := 2 object["y"]+x;

Використовуйте UpValues ​​[type], щоб побачити, що функція та function2 визначені в символі типу.

Подальші ідеї щодо цього останнього синтаксису представлені тут https://mathematica.stackexchange.com/a/999/66 .

Поліпшена версія SelectEquivalents

@rcollyer: Велике спасибі за те, що вивели SelectEquivalents на поверхню, це дивовижна функція. Ось покращена версія SelectEquivalents, перерахована вище, з більшою кількістю можливостей та використання опцій, це полегшує використання.

Options[SelectEquivalents] = 
   {
      TagElement->Identity,
      TransformElement->Identity,
      TransformResults->(#2&) (*#1=tag,#2 list of elements corresponding to tag*),
      MapLevel->1,
      TagPattern->_,
      FinalFunction->Identity
   };

SelectEquivalents[x_List,OptionsPattern[]] := 
   With[
      {
         tagElement=OptionValue@TagElement,
         transformElement=OptionValue@TransformElement,
         transformResults=OptionValue@TransformResults,
         mapLevel=OptionValue@MapLevel,
         tagPattern=OptionValue@TagPattern,
         finalFunction=OptionValue@FinalFunction
      }
      ,
      finalFunction[
         Reap[
            Map[
               Sow[
                  transformElement@#
                  ,
                  {tagElement@#}
               ]&
               , 
               x
               , 
               {mapLevel}
            ] 
            , 
            tagPattern
            , 
            transformResults
         ][[2]]
      ]
   ];

Ось приклади використання цієї версії:

Використання Mathematica Збір / Збір належним чином

Як би ви зробили функцію PivotTable у Mathematica?

Алгоритм швидкого двошарового бітування Mathematica

Внутрішня

Даніель Ліхтблау описує тут цікаву внутрішню структуру даних для зростаючих списків.

Реалізація квадрату в математиці

Функції налагодження

Ці два повідомлення вказують на корисні функції для налагодження:

Як налагоджувати при написанні малих чи великих кодів за допомогою Mathematica? верстат? mma налагоджувач? чи щось інше? (Показати)

/programming/5459735/the-clearest-way-to-represent-mathematicas-evaluation-sequence/5527117#5527117 (TraceView)

Ось ще одна функція, заснована на Reap і Sow, щоб витягувати вирази з різних частин програми та зберігати їх у символі.

SetAttributes[ReapTags,HoldFirst];
ReapTags[expr_]:=
   Module[{elements},
      Reap[expr,_,(elements[#1]=#2/.{x_}:>x)&];
      elements
   ];

Ось приклад

ftest[]:=((*some code*)Sow[1,"x"];(*some code*)Sow[2,"x"];(*some code*)Sow[3,"y"]);
s=ReapTags[ftest[]];
Keys[s]
s["x"]
PrintSymbol[s] (*Keys and PrintSymbol are defined above*)

Інші ресурси

Ось перелік цікавих посилань для навчальних цілей:

Збірник навчальних ресурсів Mathematica

Оновлено тут: https://mathematica.stackexchange.com/a/259/66


Пов'язане: " Найкращий спосіб побудувати функцію з пам'яттю ". WReach подав там дивовижний приклад простої функції, яка не тільки запам'ятовує її значення, але й записує їх у файл і читає назад при перезапуску.
Олексій Попков

1
Пов'язане: " Mathematica: Як очистити кеш-пам'ять для символу, тобто Скинути DownValues ​​без візерунка ". Це питання показує, як очистити кеш-пам'ять за допомогою стандартної f[x_] := f[x] = some codeпам'яті.
Саймон

7
+1 Існує хороший Notational зручність , що позбавляє від необхідності повторювати ліві з визначення в функції кешування, наприклад: c:Cache[expr_] := c = expr.
WReach

Хороший варіант SelectEquivalents. Думаю, я хотів би зберегти TagOnElementяк другий параметр, який дефолтом Identityвважає, що саме він використовується найчастіше. Я також не думаю, що я включив FinalOpце, як це можна вирішити в межах OpOnTaggedElems. Я б також скоротив імена варіантів, оскільки їх довжина робить це незручно набирати. Спробуйте TagFunction, TransformElement, TransformResultsі TagPatternзамість цього. І те, TagPatternі MapLevelчудове доповнення до функціональності та гарне перезапис у цілому.
rcollyer

Дякуємо за ваш коментар rcollyer. Я врахував це і покращив також читабельність коду. Я зберігаю FinalFunction, оскільки він працює на результаті Reap, наприклад, якщо ви хочете сортувати свій кінцевий результат за тегами, якщо ви їх зберігаєте.
faysou

12

Мої функції утиліти (у мене вони вбудовані в MASH, про що йдеться у запитанні):

pr = WriteString["stdout", ##]&;            (* More                           *)
prn = pr[##, "\n"]&;                        (*  convenient                    *)
perr = WriteString["stderr", ##]&;          (*   print                        *)
perrn = perr[##, "\n"]&;                    (*    statements.                 *)
re = RegularExpression;                     (* I wish mathematica             *)
eval = ToExpression[cat[##]]&;              (*  weren't so damn               *)
EOF = EndOfFile;                            (*   verbose!                     *)
read[] := InputString[""];                  (* Grab a line from stdin.        *)
doList[f_, test_] :=                        (* Accumulate list of what f[]    *)
  Most@NestWhileList[f[]&, f[], test];      (*  returns while test is true.   *)
readList[] := doList[read, #=!=EOF&];       (* Slurp list'o'lines from stdin. *)
cat = StringJoin@@(ToString/@{##})&;        (* Like sprintf/strout in C/C++.  *)
system = Run@cat@##&;                       (* System call.                   *)
backtick = Import[cat["!", ##], "Text"]&;   (* System call; returns stdout.   *)
slurp = Import[#, "Text"]&;                 (* Fetch contents of file as str. *)
                                            (* ABOVE: mma-scripting related.  *)
keys[f_, i_:1] :=                           (* BELOW: general utilities.      *)
  DownValues[f, Sort->False][[All,1,1,i]];  (* Keys of a hash/dictionary.     *)
SetAttributes[each, HoldAll];               (* each[pattern, list, body]      *)
each[pat_, lst_, bod_] := ReleaseHold[      (*  converts pattern to body for  *)
  Hold[Cases[Evaluate@lst, pat:>bod];]];    (*   each element of list.        *)
some[f_, l_List] := True ===                (* Whether f applied to some      *)
  Scan[If[f[#], Return[True]]&, l];         (*  element of list is True.      *)
every[f_, l_List] := Null ===               (* Similarly, And @@ f/@l         *)
  Scan[If[!f[#], Return[False]]&, l];       (*  (but with lazy evaluation).   *)

Для «кожної» функції см stackoverflow.com/questions/160216/foreach-loop-in-mathematica
dreeves

11

Я використовував один трюк, який дозволяє наслідувати спосіб роботи більшості вбудованих функцій із поганими аргументами (відправляючи повідомлення та повертаючи всю оцінку в неоціненому вигляді), використовує вигадку способу Conditionроботи при використанні у визначенні. Якщо fooслід працювати лише з одним аргументом:

foo[x_] := x + 1;
expr : foo[___] /; (Message[foo::argx, foo, Length@Unevaluated[expr], 1]; 
                    False) := Null; (* never reached *)

Якщо у вас є складніші потреби, легко визначити перевірку аргументів та генерацію повідомлень як незалежну функцію. Ви можете зробити більш детальні речі, використовуючи побічні ефекти, Conditionкрім того, щоб просто генерувати повідомлення, але, на мою думку, більшість з них потрапляють у категорію "кривдний злом" і слід уникати, якщо це можливо.

Крім того, у категорії "метапрограмування", якщо у вас є файл пакету Mathematica ( .m), ви можете використовувати "HeldExpressions"елемент, щоб отримати всі вирази у файлі, загорнутому HoldComplete. Це набагато простіше відстежувати речі, ніж використання текстового пошуку. На жаль, не існує простого способу зробити те ж саме з ноутбуком, але ви можете отримати всі вхідні вирази, використовуючи щось таке:

inputExpressionsFromNotebookFile[nb_String] :=
 Cases[Get[nb],
  Cell[BoxData[boxes_], "Input", ___] :>
   MakeExpression[StripBoxes[boxes], StandardForm],
  Infinity]

Нарешті, ви можете використовувати той факт, що Moduleімітує лексичні закриття, щоб створити еквівалент посилальних типів. Ось простий стек (який використовує варіацію Conditionхитрості для обробки помилок як бонус):

ClearAll[MakeStack, StackInstance, EmptyQ, Pop, Push, Peek]
 With[{emptyStack = Unique["empty"]},
  Attributes[StackInstance] = HoldFirst;
  MakeStack[] :=
   Module[{backing = emptyStack},
    StackInstance[backing]];

  StackInstance::empty = "stack is empty";

  EmptyQ[StackInstance[backing_]] := (backing === emptyStack);

  HoldPattern[
    Pop[instance : StackInstance[backing_]]] /;
    ! EmptyQ[instance] || (Message[StackInstance::empty]; False) :=
   (backing = Last@backing; instance);

  HoldPattern[Push[instance : StackInstance[backing_], new_]] :=
   (backing = {new, backing}; instance);

  HoldPattern[Peek[instance : StackInstance[backing_]]] /;
    ! EmptyQ[instance] || (Message[StackInstance::empty]; False) :=
   First@backing]

Тепер ви можете надрукувати елементи списку у зворотному порядку без потреби.

With[{stack = MakeStack[], list},
 Do[Push[stack, elt], {elt, list}];

 While[!EmptyQ[stack],
  Print[Peek@stack];
  Pop@stack]]

1
+1 для HeldExpressionsелемента в пакунках, про це не знав. Я зазвичай імпортування в вигляді рядка , а потім використовувати ToExpressionз HoldCompleteякості останнього арг. Щодо використання Conditionдля повідомлень - це була стандартна методика написання пакунків, щонайменше, з 1994 року. Щодо наполегливості через Moduleрізноманітність - я вже давно писав про це в Mathgroup: групи.google.com/group/comp.soft- sys.math.mathematica /… (мій третій пост у цій темі), це уздовж цих же ліній і містить посилання на кілька нетривіальних прикладів використання.
Леонід Шифрін

@ Леонід Шифрін: Я підібрав Conditionріч як лор, ймовірно, від колеги, але не зрозумів, що це стандартна техніка. ModuleЦікаве посилання про використання символів як еталонних типів!
Pillsy

+1, я ніколи про це не думав. Чим більше я дізнаюся про цю мову, тим сильнішою вона здається.
rcollyer

@Pillsy, яка мета зробити стек таким чином?
Mr.Wizard

@ Mr.Wizard: Я просто обрав одну з найпростіших змінних структур даних, про які я міг придумати, щоб проілюструвати методику.
Пілсі

11

Визначення символів системи друку без контексту

contextFreeDefinition[]Нижче функція буде намагатися надрукувати визначення символу без найбільш загального контексту предварённого. Потім визначення можна скопіювати в Workbench і відформатувати для читабельності (виберіть його, клацніть правою кнопкою миші, Джерело -> Формат)

Clear[commonestContexts, contextFreeDefinition]

commonestContexts[sym_Symbol, n_: 1] := Quiet[
  Commonest[
   Cases[Level[DownValues[sym], {-1}, HoldComplete], 
    s_Symbol /; FreeQ[$ContextPath, Context[s]] :> Context[s]], n],
  Commonest::dstlms]

contextFreeDefinition::contexts = "Not showing the following contexts: `1`";

contextFreeDefinition[sym_Symbol, contexts_List] := 
 (If[contexts =!= {}, Message[contextFreeDefinition::contexts, contexts]];
  Internal`InheritedBlock[{sym}, ClearAttributes[sym, ReadProtected];
   Block[{$ContextPath = Join[$ContextPath, contexts]}, 
    Print@InputForm[FullDefinition[sym]]]])

contextFreeDefinition[sym_Symbol, context_String] := 
 contextFreeDefinition[sym, {context}]

contextFreeDefinition[sym_Symbol] := 
 contextFreeDefinition[sym, commonestContexts[sym]]

withRules []

Застереження: Ця функція не призначена локалізує змінні так само , як Withі Moduleроблять, що означає , що вкладені конструкції локалізації не буде роботи , як і очікувалося. withRules[{a -> 1, b -> 2}, With[{a=3}, b_ :> b]] буде замінити aі bв вкладеному Withі Rule, в той час як Withне робить цього.

Це варіант , Withякий використовує правила замість =і :=:

ClearAll[withRules]
SetAttributes[withRules, HoldAll]
withRules[rules_, expr_] :=
  Internal`InheritedBlock[
    {Rule, RuleDelayed},
    SetAttributes[{Rule, RuleDelayed}, HoldFirst];
    Unevaluated[expr] /. rules
  ]

Я вважаю це корисним при очищенні коду, написаного під час експерименту та локалізації змінних. Інколи я закінчую списки параметрів у вигляді {par1 -> 1.1, par2 -> 2.2}. За допомогою withRulesзначень параметрів легко вводити в код, записаний раніше за допомогою глобальних змінних.

Використання так само With:

withRules[
  {a -> 1, b -> 2},
  a+b
]

Антиализационная 3D-графіка

Це дуже проста методика антиалійської 3D-графіки, навіть якщо ваше графічне обладнання не підтримує її в оригіналі.

antialias[g_, n_: 3] := 
  ImageResize[Rasterize[g, "Image", ImageResolution -> n 72], Scaled[1/n]]

Ось приклад:

Графіка математики Графіка математики

Зауважте, що велике значення nабо великий розмір зображення має тенденцію виявляти помилки драйверів графіки або вводити артефакти.


Функціональність ноутбука відрізняється

Функціональність для ноутбуків відрізняється наявністю в <<AuthorTools`пакеті та (принаймні у версії 8) у недокументованому NotebookTools`контексті. Це невеликий графічний інтерфейс, щоб відрізняти два ноутбуки, які зараз відкриті:

PaletteNotebook@DynamicModule[
  {nb1, nb2}, 
  Dynamic@Column[
    {PopupMenu[Dynamic[nb1], 
      Thread[Notebooks[] -> NotebookTools`NotebookName /@ Notebooks[]]], 
     PopupMenu[Dynamic[nb2], 
      Thread[Notebooks[] -> NotebookTools`NotebookName /@ Notebooks[]]], 
     Button["Show differences", 
      CreateDocument@NotebookTools`NotebookDiff[nb1, nb2]]}]
  ]

Графіка математики


Все було б добре, але це насправді не локалізує змінні, як ви бачите, призначивши припущення a = 3; b = 4;до вашого прикладу виклику, а потім зателефонувавши withRules. Ви можете зберегти його, використовуючи замість цього наступне: SetAttributes[withRules, HoldAll];withRules[rules_, expr_] := Unevaluated[expr] /. Unevaluated[rules]. Відмінності wrt семантики Withтоді: 1. rhsides правил зараз не оцінюється 2. withRulesне вирішує конфлікти імен з внутрішніми масштабуючими конструкціями, як Withце відбувається. Останній досить серйозний - добре чи погано, залежно від випадку.
Леонід Шифрін

@Leonid Ви абсолютно праві, здається, я ніколи не дізнаюся про правильну перевірку коду перед публікацією ... коли я використовую це, я практично ніколи не присвоюю значенням змінним, але це досить серйозна проблема, ви маєте рацію. Що ви думаєте про виправлену версію? (Мені не дуже важливо не обробляти вкладені Withs. Це не завжди працює і з вбудованими конструкціями локалізації, наприклад With[{a=1}, Block[{a=2}, a]]. Чи вважаєте ви, що є вагомі причини, чому вкладене місце Blockне локалізується, як вкладене Withі Moduleтак?)
Szabolcs

@ Леонід я не просто використовував, Unevaluated[rules]тому що хотів x -> 1+1оцінити РЗС.
Szabolcs

@Leonid Ви насправді маєте рацію, проблема вкладеної локалізації може бути досить серйозною. Я думаю, що вкладені Withs легко помітити та уникнути, але закономірностей немає: With[{a = 1}, a_ -> a]локалізується внутрішня, aа withRulesне. Чи знаєте ви, чи є спосіб отримати доступ до внутрішнього механізму локалізації Mathematica та створити нові конструкції (подібні до Rule), які також локалізуються? Цю відповідь я, мабуть, видалю пізніше, оскільки це небезпечніше, ніж корисно, але спочатку я хотів би пограти з нею трохи більше.
Сабольч

Я думаю, що ваше використання InheritedBlockдосить класне і вирішує проблему дуже елегантно. Що стосується сфери конфліктів, зазвичай, прив'язки до лексичного обстеження відбуваються в "час лексичного зв’язування", який є - до часу виконання, тоді як динамічне масштабування пов'язується під час виконання, що може пояснити це. Ви можете порівняти це з аналогічним випадком Module, який дозволяє конструктивно використовувати (див., Наприклад, тут stackoverflow.com/questions/7394113/… ). Проблема в тому, що Blockпотрібен якийсь символ, щоб ...
Леонід Шифрін

9

Рекурсивні чисті функції ( #0) здаються одним із темних куточків мови. Ось пара нетривіальних прикладів їх використання, де це справді корисно (не те, що без нього не обійтися). Далі наведена досить коротка та досить швидка функція пошуку з’єднаних компонентів у графі, з урахуванням списку ребер, вказаних у вигляді пар вершин:

ClearAll[setNew, componentsBFLS];
setNew[x_, x_] := Null;
setNew[lhs_, rhs_]:=lhs:=Function[Null, (#1 := #0[##]); #2, HoldFirst][lhs, rhs];

componentsBFLS[lst_List] := Module[{f}, setNew @@@ Map[f, lst, {2}];
   GatherBy[Tally[Flatten@lst][[All, 1]], f]];

Тут відбувається те, що ми спочатку відображаємо символ манекена на кожне з номерів вершин, а потім встановлюємо спосіб, який, даючи пару вершин {f[5],f[10]}, скажімо, потім f[5]оцінював би f[10]. Рекурсивна чиста функція використовується як компресор шляху (для встановлення пам'яті таким чином, щоб замість довгих ланцюгів, як f[1]=f[3],f[3]=f[4],f[4]=f[2], ..., наприклад , запам'ятовані значення виправлялися щоразу, коли виявлявся новий "корінь" компонента. Це дає значне прискорення. Оскільки ми використовуємо завдання, нам це потрібно HoldAll, що робить цю конструкцію ще більш незрозумілою та привабливішою). Ця функція є результатом онлайн-дискусії про Mathgroup та за її межами, в якій беруть участь Фред Сімонс, Саболч Хорват, ДрМайорБоб та Ваші справді. Приклад:

In[13]:= largeTest=RandomInteger[{1,80000},{40000,2}];

In[14]:= componentsBFLS[largeTest]//Short//Timing
Out[14]= {0.828,{{33686,62711,64315,11760,35384,45604,10212,52552,63986,  
     <<8>>,40962,7294,63002,38018,46533,26503,43515,73143,5932},<<10522>>}}

Це, звичайно, набагато повільніше, ніж вбудований, але за розміром коду, досить швидкий IMO.

Інший приклад: ось рекурсивна реалізація на Selectоснові пов'язаних списків та рекурсивних чистих функцій:

selLLNaive[x_List, test_] :=
  Flatten[If[TrueQ[test[#1]],
     {#1, If[#2 === {}, {}, #0 @@ #2]},
     If[#2 === {}, {}, #0 @@ #2]] & @@ Fold[{#2, #1} &, {}, Reverse[x]]];

Наприклад,

In[5]:= Block[
         {$RecursionLimit= Infinity},
         selLLNaive[Range[3000],EvenQ]]//Short//Timing

Out[5]= {0.047,{2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,
 <<1470>>,2972,2974,2976,2978,2980,2982,2984,2986,2988,2990,
  2992,2994,2996,2998,3000}}

Однак він не є належним чином рекурсивним, а також видалить стек (розбить ядро) для більших списків. Ось хвостово-рекурсивна версія:

selLLTailRec[x_List, test_] :=
Flatten[
 If[Last[#1] === {},
  If[TrueQ[test[First[#1]]],
   {#2, First[#1]}, #2],
  (* else *)
  #0[Last[#1],
   If[TrueQ[test[First[#1]]], {#2, First[#1]}, #2]
   ]] &[Fold[{#2, #1} &, {}, Reverse[x]], {}]];

Наприклад,

In[6]:= Block[{$IterationLimit= Infinity},
       selLLTailRec[Range[500000],EvenQ]]//Short//Timing
Out[6]= {2.39,{2,4,6,8,10,12,14,16,18,20,22,
       <<249978>>,499980,499982,499984,499986,499988,499990,499992,
        499994,499996,499998,500000}} 

Функція підключених компонентів досі є моїм улюбленим :-)
Szabolcs

@Szabolcs Так, це досить круто. Ви з Фредом зробили більшу частину цього, Боббі і я лише доповнили кілька уточнень, IIRC.
Леонід Шифрін

8

Це рецепт із книги Стен Вагона ... використовуйте його, коли вбудований сюжет поводиться нестабільно через недостатню точність

Options[PrecisePlot] = {PrecisionGoal -> 6};
PrecisePlot[f_, {x_, a_, b_}, opts___] := Module[{g, pg},
   pg = PrecisionGoal /. {opts} /. Options[PrecisePlot];
   SetAttributes[g, NumericFunction];
   g[z_?InexactNumberQ] := Evaluate[f /. x -> z];
   Plot[N[g[SetPrecision[y, \[Infinity]]], pg], {y, a, b},
    Evaluate[Sequence @@ FilterRules[{opts}, Options[Plot]]]]];

Я часто використовую наступний трюк від Кріджана Каніке, коли мені потрібна поведінка, схожа на словник, з нижчих значень Mathematica

index[downvalue_, 
   dict_] := (downvalue[[1]] /. HoldPattern[dict[x_]] -> x) // 
   ReleaseHold;
value[downvalue_] := downvalue[[-1]];
indices[dict_] := 
  Map[#[[1]] /. {HoldPattern[dict[x_]] -> x} &, DownValues[dict]] // 
   ReleaseHold;
values[dict_] := Map[#[[-1]] &, DownValues[dict]];
items[dict_] := Map[{index[#, dict], value[#]} &, DownValues[dict]];
indexQ[dict_, index_] := 
  If[MatchQ[dict[index], HoldPattern[dict[index]]], False, True];

(* Usage example: *)
(* Count number of times each subexpression occurs in an expression *)
expr = Cos[x + Cos[Cos[x] + Sin[x]]] + Cos[Cos[x] + Sin[x]]
Map[(counts[#] = If[indexQ[counts, #], counts[#] + 1, 1]; #) &, expr, Infinity];
items[counts]

Якщо результати оцінки заплутані, іноді це допомагає скинути етапи оцінки в текстовий файл

SetAttributes[recordSteps, HoldAll];
recordSteps[expr_] :=
 Block[{$Output = List@OpenWrite["~/temp/msgStream.m"]}, 
  TracePrint[Unevaluated[expr], _?(FreeQ[#, Off] &), 
   TraceInternal -> True];
  Close /@ $Output;
  Thread[Union@
    Cases[ReadList["~/temp/msgStream.m", HoldComplete[Expression]], 
     symb_Symbol /; 
       AtomQ@Unevaluated@symb && 
        Context@Unevaluated@symb === "System`" :> 
      HoldComplete@symb, {0, Infinity}, Heads -> True], HoldComplete]
  ]

(* Usage example: *)
(* puts steps of evaluation of 1+2+Sin[5]) into ~/temp/msgStream.m *)
recordSteps[1+2+Sin[5]]

Зразок використання був би чудовим. Спробуйте опублікувати його, коли у вас є час.
Доктор Белісарій

Ти знаєш Крістіян? Раніше я працював в одній групі з ним у Гельсінкі. Гарний хлопець, маленький світ.
Тимо

Ні, знайшов його код в Інтернеті. Насправді намагалися надіслати йому електронну пошту, щоб виправити невелику помилку в коді, але електронний лист на його веб-сторінці більше не працює
Ярослав Булатов

8

Можна запустити MathKernel в пакетному режимі, використовуючи недокументовані параметри командного рядка -batchinputта-batchoutput :

math -batchinput -batchoutput < input.m > outputfile.txt

(де input.mфайл вхідного пакету закінчується символом нового рядка, outputfile.txtце файл, до якого буде переспрямовано вихід).

У Mathematica v.> = 6 у MathKernel є недокументований варіант командного рядка:

-noicon

який контролює, чи буде MathKernel мати видимий значок на панелі завдань (принаймні під Windows).

FrontEnd (принаймні з v.5) має опцію бездокументованого командного рядка

-b

що вимикає екран заставки і дозволяє запускати Mathematica FrontEnd набагато швидше

і варіант

-directlaunch

який відключає механізм, який запускає останню встановлену версію Mathematica замість запуску версії, пов'язаної з .nb файлами в системному реєстрі.

Можливо, ще один спосіб зробити це :

Замість запуску бінарного файлу Mathematica.exe в каталозі інсталяції запустіть двійковий файл Mathematica.exe в SystemFiles \ FrontEnd \ Binaries \ Windows. Перша - це проста програма запуску, яка намагається найскладніше перенаправити запити на відкриття ноутбуків до запущених копій користувальницького інтерфейсу. Останнє є самим бінарним інтерфейсом користувача.

Зручно поєднувати останній варіант командного рядка із встановленням глобальної опції FrontEnd, VersionedPreferences->True яка вимикає обмін налаштуваннями між різними встановленими версіями Mathematica :

SetOptions[$FrontEnd, VersionedPreferences -> True]

(Сказане вище слід оцінювати в останній встановленій версії Mathematica .)

У Mathematica 8 це регулюється в діалоговому вікні "Налаштування" на панелі "Система" під налаштуванням "Створити та підтримувати параметри переднього кінця" .

Отримати неповний список параметрів командного рядка FrontEnd можна за допомогою недокументованого ключа -h(код для Windows):

SetDirectory[$InstallationDirectory <> 
   "\\SystemFiles\\FrontEnd\\Binaries\\Windows\\"];
Import["!Mathematica -h", "Text"]

дає:

Usage:  Mathematica [options] [files]
Valid options:
    -h (--help):  prints help message
    -cleanStart (--cleanStart):  removes existing preferences upon startup
    -clean (--clean):  removes existing preferences upon startup
    -nogui (--nogui):  starts in a mode which is initially hidden
    -server (--server):  starts in a mode which disables user interaction
    -activate (--activate):  makes application frontmost upon startup
    -topDirectory (--topDirectory):  specifies the directory to search for resources and initialization files
    -preferencesDirectory (--preferencesDirectory):  specifies the directory to search for user AddOns and preference files
    -password (--password):  specifies the password contents
    -pwfile (--pwfile):  specifies the path for the password file
    -pwpath (--pwpath):  specifies the directory to search for the password file
    -b (--b):  launches without the splash screen
    -min (--min):  launches as minimized

Інші варіанти включають:

-directLaunch:  force this FE to start
-32:  force the 32-bit FE to start
-matchingkernel:  sets the frontend to use the kernel of matching bitness
-Embedding:  specifies that this instance is being used to host content out of process

Чи є інші потенційно корисні параметри командного рядка MathKernel і FrontEnd? Будь ласка, поділіться, якщо знаєте.

Супутнє питання .


"відповідність біт?" Що це означає?
Mr.Wizard

@ Mr.Wizard Ймовірно, ця опція має сенс лише під 64-бітовими системами в поєднанні з опцією -32і означає, що біт MathKernel, який використовується FrontEnd, буде відповідати біттю операційної системи (64 біт). Здається, що в інших випадках цей варіант нічого не змінить.
Олексій Попков

7

Мої улюблені хаки - це невеликі макроси, що генерують код, які дозволяють замінити купу стандартних команд котельної панелі на одну коротку. Крім того, ви можете створювати команди для відкриття / створення зошитів.

Ось що я використовував деякий час у своєму щоденному робочому процесі Mathematica. Я виявив, що виконую наступне:

  1. Зробіть, щоб ноутбук мав приватний контекст, завантажуйте потрібні пакунки, зробіть його автоматичним збереженням.
  2. Попрацювавши з цим ноутбуком деякий час, я хотів би зробити кілька обчислень з подряпин в окремому зошиті з власним приватним контекстом, маючи доступ до визначень, які я використовував у "головному" ноутбуці. Оскільки я налаштовую приватний контекст, для цього потрібно вручну налаштувати $ ContextPath

Робити все це вручну і знову - це біль, тому давайте автоматизуємо! По-перше, кілька корисних кодів:

(* Credit goes to Sasha for SelfDestruct[] *)
SetAttributes[SelfDestruct, HoldAllComplete];
SelfDestruct[e_] := (If[$FrontEnd =!= $Failed,
   SelectionMove[EvaluationNotebook[], All, EvaluationCell]; 
   NotebookDelete[]]; e)

writeAndEval[nb_,boxExpr_]:=(
    NotebookWrite[nb,  CellGroupData[{Cell[BoxData[boxExpr],"Input"]}]];
    SelectionMove[nb, Previous, Cell]; 
    SelectionMove[nb, Next, Cell];
    SelectionEvaluate[nb];
)

ExposeContexts::badargs = 
  "Exposed contexts should be given as a list of strings.";
ExposeContexts[list___] := 
 Module[{ctList}, ctList = Flatten@List@list; 
  If[! MemberQ[ctList, Except[_String]],AppendTo[$ContextPath, #] & /@ ctList, 
   Message[ExposeContexts::badargs]];
  $ContextPath = DeleteDuplicates[$ContextPath];
  $ContextPath]

    Autosave[x:(True|False)] := SetOptions[EvaluationNotebook[],NotebookAutoSave->x];

Тепер давайте створимо макрос, який збирається помістити наступні комірки в зошит:

SetOptions[EvaluationNotebook[], CellContext -> Notebook]
Needs["LVAutils`"]
Autosave[True]

І ось макрос:

MyPrivatize[exposedCtxts : ({__String} | Null) : Null]:=
  SelfDestruct@Module[{contBox,lvaBox,expCtxtBox,assembledStatements,strList},
    contBox = MakeBoxes[SetOptions[EvaluationNotebook[], CellContext -> Notebook]];
    lvaBox = MakeBoxes[Needs["LVAutils`"]];

    assembledStatements = {lvaBox,MakeBoxes[Autosave[True]],"(*********)"};
    assembledStatements = Riffle[assembledStatements,"\[IndentingNewLine]"]//RowBox;
    writeAndEval[InputNotebook[],contBox];
    writeAndEval[InputNotebook[],assembledStatements];
    If[exposedCtxts =!= Null,
       strList = Riffle[("\"" <> # <> "\"") & /@ exposedCtxts, ","];
       expCtxtBox = RowBox[{"ExposeContexts", "[", RowBox[{"{", RowBox[strList], "}"}], "]"}];
       writeAndEval[InputNotebook[],expCtxtBox];
      ]
 ]

Тепер, коли я набираю текст MyPrivatize[], створює приватний контекст і завантажує мій стандартний пакет. Тепер давайте створимо команду, яка відкриє новий ноутбук скретчів із власним приватним контекстом (так що ви можете зламати там дикі відмови, не ризикуючи викрутити визначення), але матиме доступ до ваших поточних контекстів.

SpawnScratch[] := SelfDestruct@Module[{nb,boxExpr,strList},
    strList = Riffle[("\"" <> # <> "\"") & /@ $ContextPath, ","];
    boxExpr = RowBox[{"MyPrivatize", "[",
        RowBox[{"{", RowBox[strList], "}"}], "]"}];
    nb = CreateDocument[];
    writeAndEval[nb,boxExpr];
]

Класна річ у цьому полягає в тому, що через SelfDestructте, що команда виконує, вона не залишає слідів у поточному ноутбуці - це добре, бо в іншому випадку це просто створить безлад.

Для додаткових точок стилю ви можете створити тригери ключових слів для цих макросів, використовуючи InputAutoReplacements, але я залишу це як вправу для читача.


7

PutAppend with PageWidth -> Нескінченність

У Mathematica використання PutAppendкоманди є найпростішим способом підтримання запущеного файлу журналу з результатами проміжних обчислень. Але він використовує за замовчуванням PageWith->78налаштування під час експорту виразів у файл, тому немає гарантії, що кожен проміжний висновок займе лише один рядок у журналі.

PutAppendне має самих варіантів, але відстежуючи його оцінки, виявляється, що він заснований на OpenAppendфункції, яка має PageWithопцію, і дозволяє змінювати значення за замовчуванням SetOptionsкомандою:

In[2]:= Trace[x>>>"log.txt",TraceInternal->True]
Out[2]= {x>>>log.txt,{OpenAppend[log.txt,CharacterEncoding->PrintableASCII],OutputStream[log.txt,15]},Null}

Таким чином, ми можемо PutAppendдодати за один раз лише один рядок, встановивши:

SetOptions[OpenAppend, PageWidth -> Infinity]

ОНОВЛЕННЯ

Існує помилка, введена у версії 10 (виправлена ​​у версії 11.3): SetOptionsбільше не впливає на поведінку OpenWriteта OpenAppend.

Вирішення проблеми полягає в тому, щоб реалізувати власну версію програми PutAppendз явним PageWidth -> Infinityваріантом:

Clear[myPutAppend]
myPutAppend[expr_, pathtofile_String] :=
 (Write[#, expr]; Close[#];) &[OpenAppend[pathtofile, PageWidth -> Infinity]]

Зауважте, що ми також можемо реалізувати його через, WriteStringяк показано у цій відповіді, але в цьому випадку необхідно попередньо перетворити вираз у відповідний InputFormvia ToString[expr, InputForm].


6

Я просто дивлюся через одну з моїх пакетів для занесення до цього, і знайшов деякі повідомлення , які я визначив , що творять чудеса: Debug::<some name>. За замовчуванням вони вимкнені, тому не створюйте великих витрат. Але я можу заповнити їх своїм кодом і ввімкнути їх, якщо мені потрібно зрозуміти, як саме поводиться трохи коду.


З довідки> з версії 2.0 (випущена в 1991 р.) Налагодження замінено Trace.
Доктор Белісарій

1
@belisarius, ти пропустив пункт. Це Debugні Traceфункції, ні функції; це набір повідомлень, які я створив, за допомогою яких я можу ввімкнути / вимкнути свій код. Вони є збірними зі словом Debug, так само, як usagemsg є попередньою для назви функції. Він надає таку ж функціональність, як розміщення купу coutвисловлювань у коді c ++.
rcollyer

1
Ох ... вибачте. Я заплутався, бо я ніколи не закінчив дитячий садок за те, що не навчався "Столиці для країн": D
Доктор Белісарій,

6

Однією з речей, яка мене турбує щодо вбудованих конструкцій масштабування, є те, що вони оцінюють усі визначення локальних змінних одразу, тому не можна писати, наприклад,

With[{a = 5, b = 2 * a},
    ...
]

Тож деякий час тому я придумав макрос під назвою WithNest, який дозволяє це зробити. Я вважаю це зручним, оскільки дозволяє зберігати мінливі прив'язки локально, не роблячи щось подібне

Module[{a = 5,b},
    b = 2 * a;
    ...
]

Врешті-решт, найкращим способом, який я міг би зробити це, було використання спеціального символу, щоб полегшити повторний перелік списків прив’язок, і я вклав визначення у власний пакет, щоб цей символ був прихованим. Може у когось є більш просте рішення цієї проблеми?

Якщо ви хочете спробувати це, поставте у файл, який називається Scoping.m:

BeginPackage["Scoping`"];

WithNest::usage=
"WithNest[{var1=val1,var2=val2,...},body] works just like With, except that
values are evaluated in order and later values have access to earlier ones.
For example, val2 can use var1 in its definition.";

Begin["`Private`"];

(* Set up a custom symbol that works just like Hold. *)
SetAttributes[WithNestHold,HoldAll];

(* The user-facing call.  Give a list of bindings and a body that's not
our custom symbol, and we start a recursive call by using the custom
symbol. *)
WithNest[bindings_List,body:Except[_WithNestHold]]:=
WithNest[bindings,WithNestHold[body]];

(* Base case of recursive definition *)
WithNest[{},WithNestHold[body_]]:=body;

WithNest[{bindings___,a_},WithNestHold[body_]]:=
WithNest[
{bindings},
WithNestHold[With[List@a,body]]];

SyntaxInformation[WithNest]={"ArgumentsPattern"->{{__},_}};
SetAttributes[WithNest,{HoldAll,Protected}];

End[];

EndPackage[];

Janus опублікував версію цього, і посилається на своє питання на MathGroup: stackoverflow.com/questions/4190845/custom-notation-question / ...
Mr.Wizard

Дякуємо, що вказали на це! Минув час, коли я переглянув цей матеріал, і цікаво бачити всі ці інші підходи.
DGrady

5

Цей автор написав Альберто Ді Лулло (який, схоже, не перебуває у переповненні стека).

CopyToClipboard, для Mathematica 7 (у Mathematica 8 вбудований)

CopyToClipboard[expr_] := 
  Module[{nb}, 
   nb = CreateDocument[Null, Visible -> False, WindowSelected -> True];
   NotebookWrite[nb, Cell[OutputFormData@expr], All];
   FrontEndExecute[FrontEndToken[nb, "Copy"]];
   NotebookClose@nb];

Оригінальна публікація: http://forums.wolfram.com/mathgroup/archive/2010/Jun/msg00148.html

Я вважаю цю процедуру корисною для копіювання великих реальних цифр у буфер обміну в звичайній десятковій формі. НапрCopyToClipboard["123456789.12345"]

Cell[OutputFormData@expr] акуратно прибирає цитати.


5

Цей код складає палітру, яка завантажує вибір у Stack Exchange як зображення. У Windows передбачена додаткова кнопка, яка дає більш вірне відображення вибору.

Скопіюйте код у комірку зошита та оцініть. Потім виведіть палітру з виводу та встановіть її за допомогоюPalettes -> Install Palette...

Якщо у вас виникли проблеми з цим, опублікуйте коментар тут. Завантажте версію для ноутбука тут .


Begin["SOUploader`"];

Global`palette = PaletteNotebook@DynamicModule[{},

   Column[{
     Button["Upload to SE",
      With[{img = rasterizeSelection1[]},
       If[img === $Failed, Beep[], uploadWithPreview[img]]],
      Appearance -> "Palette"],

     If[$OperatingSystem === "Windows",

      Button["Upload to SE (pp)",
       With[{img = rasterizeSelection2[]},
        If[img === $Failed, Beep[], uploadWithPreview[img]]],
       Appearance -> "Palette"],

      Unevaluated@Sequence[]
      ]
     }],

   (* Init start *)
   Initialization :>
    (

     stackImage::httperr = "Server returned respose code: `1`";
     stackImage::err = "Server returner error: `1`";

     stackImage[g_] :=
      Module[
       {getVal, url, client, method, data, partSource, part, entity,
        code, response, error, result},

       getVal[res_, key_String] :=
        With[{k = "var " <> key <> " = "},
         StringTrim[

          First@StringCases[
            First@Select[res, StringMatchQ[#, k ~~ ___] &],
            k ~~ v___ ~~ ";" :> v],
          "'"]
         ];

       data = ExportString[g, "PNG"];

       JLink`JavaBlock[
        url = "http://stackoverflow.com/upload/image";
        client =
         JLink`JavaNew["org.apache.commons.httpclient.HttpClient"];
        method =
         JLink`JavaNew[
          "org.apache.commons.httpclient.methods.PostMethod", url];
        partSource =
         JLink`JavaNew[
          "org.apache.commons.httpclient.methods.multipart.\
ByteArrayPartSource", "mmagraphics.png",
          JLink`MakeJavaObject[data]@toCharArray[]];
        part =
         JLink`JavaNew[
          "org.apache.commons.httpclient.methods.multipart.FilePart",
          "name", partSource];
        part@setContentType["image/png"];
        entity =
         JLink`JavaNew[
          "org.apache.commons.httpclient.methods.multipart.\
MultipartRequestEntity", {part}, method@getParams[]];
        method@setRequestEntity[entity];
        code = client@executeMethod[method];
        response = method@getResponseBodyAsString[];
        ];

       If[code =!= 200, Message[stackImage::httperr, code];
        Return[$Failed]];
       response = StringTrim /@ StringSplit[response, "\n"];

       error = getVal[response, "error"];
       result = getVal[response, "result"];
       If[StringMatchQ[result, "http*"],
        result,
        Message[stackImage::err, error]; $Failed]
       ];

     stackMarkdown[g_] :=
      "![Mathematica graphics](" <> stackImage[g] <> ")";

     stackCopyMarkdown[g_] := Module[{nb, markdown},
       markdown = Check[stackMarkdown[g], $Failed];
       If[markdown =!= $Failed,
        nb = NotebookCreate[Visible -> False];
        NotebookWrite[nb, Cell[markdown, "Text"]];
        SelectionMove[nb, All, Notebook];
        FrontEndTokenExecute[nb, "Copy"];
        NotebookClose[nb];
        ]
       ];

     (* Returns available vertical screen space,
     taking into account screen elements like the taskbar and menu *)


     screenHeight[] := -Subtract @@
        Part[ScreenRectangle /. Options[$FrontEnd, ScreenRectangle],
         2];

     uploadWithPreview[img_Image] :=
      CreateDialog[
       Column[{
         Style["Upload image to the Stack Exchange network?", Bold],
         Pane[

          Image[img, Magnification -> 1], {Automatic,
           Min[screenHeight[] - 140, 1 + ImageDimensions[img][[2]]]},
          Scrollbars -> Automatic, AppearanceElements -> {},
          ImageMargins -> 0
          ],
         Item[
          ChoiceButtons[{"Upload and copy MarkDown"}, \
{stackCopyMarkdown[img]; DialogReturn[]}], Alignment -> Right]
         }],
       WindowTitle -> "Upload image to Stack Exchange?"
       ];

     (* Multiplatform, fixed-width version.
        The default max width is 650 to fit Stack Exchange *)
     rasterizeSelection1[maxWidth_: 650] :=
      Module[{target, selection, image},
       selection = NotebookRead[SelectedNotebook[]];
       If[MemberQ[Hold[{}, $Failed, NotebookRead[$Failed]], selection],

        $Failed, (* There was nothing selected *)

        target =
         CreateDocument[{}, WindowSelected -> False, Visible -> False,
           WindowSize -> maxWidth];
        NotebookWrite[target, selection];
        image = Rasterize[target, "Image"];
        NotebookClose[target];
        image
        ]
       ];

     (* Windows-only pixel perfect version *)
     rasterizeSelection2[] :=
      If[
       MemberQ[Hold[{}, $Failed, NotebookRead[$Failed]],
        NotebookRead[SelectedNotebook[]]],

       $Failed, (* There was nothing selected *)

       Module[{tag},
        FrontEndExecute[
         FrontEndToken[FrontEnd`SelectedNotebook[], "CopySpecial",
          "MGF"]];
        Catch[
         NotebookGet@ClipboardNotebook[] /.
          r_RasterBox :>
           Block[{},
            Throw[Image[First[r], "Byte", ColorSpace -> "RGB"], tag] /;
              True];
         $Failed,
         tag
         ]
        ]
       ];
     )
   (* Init end *)
   ]

End[];

4

Я впевнений, що багато людей стикалися з ситуацією, коли вони запускають якісь речі, усвідомлюючи, що це не лише застрягло програму, але й не збереглися протягом останніх 10 хвилин!

EDIT

Постраждавши від цього деякий час, я одного дня з’ясував, що можна створити автоматичне збереження з коду Mathematica . Я думаю, що використання такого автоматичного збереження мені дуже допомагало в минулому, і я завжди відчував, що сама можливість була чимось, що не багато людей усвідомлюють, що вони можуть зробити.

Оригінальний код, який я використав, знаходиться внизу. Завдяки коментарям я з’ясував, що це проблематично і що набагато краще це зробити альтернативним способом, використовуючи ScheduledTask(що буде працювати лише в Mathematica 8).

Код для цього можна знайти в цій відповіді від Sjoerd C. de Vries (Оскільки я не впевнений, чи правильно це скопіювати, тут я залишаю лише посилання.)


Нижче наведено рішення Dynamic. Він зберігатиме ноутбук кожні 60 секунд, але, мабуть, лише у тому випадку, якщо його вікно видно . Я залишаю його тут лише для завершення. (і для користувачів Mathematica 6 та 7)

/ РЕД

Для її вирішення я використовую цей код на початку зошита:

Dynamic[Refresh[NotebookSave[]; DateString[], UpdateInterval -> 60]]

Це дозволить заощадити вашу роботу кожні 60 секунд.
Я віддаю перевагу NotebookAutoSave[]тому, що він економить перед тим, як вхід обробляється, і тому, що деякі файли - це більше тексту, ніж вхідних.

Я спочатку знайшов його тут: http://en.wikipedia.org/wiki/Talk:Mathematica#Criticisms

Зауважте, що запустивши цей рядок, збереження відбудеться, навіть якщо ви закриєте та повторно відкриєте файл (доки активізовано динамічне оновлення).

Крім того, оскільки в Mathematica немає скасування , будьте обережні, щоб не видалити весь ваш вміст, оскільки збереження зробить його незворотним (як запобіжний крок, я видаляю цей код із кожного готового ноутбука)


ви також можете зберегти його з іншим іменем (наприклад, додавши поточний час і дату до кінця імені файлу), а може бути і в певному каталозі ("Резервні копії", скажімо). це було б як примітивна форма версії.
acl

Можна зробити щось на кшталт NotebookSave[SelectedNotebook[], "work-" <> IntegerString[i] <> ".nb"]; i++, але я думаю, що будь-яке посилання на поточне ім’я ноутбука стане рекурсивним.
цвікас

2
Я думав, що Dynamicоб’єкти оновлюються лише тоді, коли вони є видимими, тому я не був би впевнений, що цей метод спрацює, якщо, наприклад, ви прокрутите Dynamicоб’єкт із видимої області. Потім знову я не пробував. У будь-якому випадку, я просто запропонував це як пропозицію.
acl

1
Ви можете перевірити це за допомогою Dynamic[Refresh[i++, UpdateInterval -> 1, TrackedSymbols -> {}]]. Прокрутіть збільшувальне число з поля зору, почекайте хвилину, прокрутіть назад і побачте, чи число не збільшується на 60. Про UpdateInterval: зазвичай це використовується, якщо можливо, але якщо ваш код включає змінні, що змінюються, ця зміна запускає нове оновлення перед проміжки закінчуються. Спробуйте вищенаведений рядок безTrackedSymbols
Шьорд К. де Вріз

1
@ j0ker5 Спробуйте мій вище код, і ви побачите, що UpdateInterval не завжди змушує оновлення розміщуватись із заданим інтервалом. Цей код також показує, що Dynamic працює лише в тому випадку, якщо клітинка, в якій він міститься, видно у фронтальній частині . Це дійсно зупиняє мить, коли вона не видно. Люди дійсно не повинні довіряти цьому коду, щоб зберегти свої файли, оскільки він не є. Його небезпечно
Шьорд К. де Вріс


3

Мені здається дуже корисним при розробці пакетів додавати цей ярлик клавіатури до мого SystemFiles/FrontEnd/TextResources/Windows/KeyEventTranslations.trфайлу.

(* Evaluate Initialization Cells: Real useful for reloading library changes. *)

Item[KeyEvent["i", Modifiers -> {Control, Command}],
    FrontEndExecute[
        FrontEndToken[
            SelectedNotebook[],
            "EvaluateInitialization"]]],

Далі для кожного Packagename.mя роблю PackagenameTest.nbзошит для тестування, і перші 2 комірки тестового зошита встановлюються як комірки ініціалізації. У першу клітинку я поставив

Needs["PackageManipulations`"]

завантажити дуже корисну бібліотеку PackageManipulations, яку написав Леонід. Друга комірка містить

PackageRemove["Packagename`Private`"]
PackageRemove["Packagename`"]
PackageReload["Packagename`"]

які всі роблять фактичне перезавантаження пакету. Зверніть увагу, що перші два рядки є лише для Removeвсіх символів, оскільки я люблю підтримувати контексти максимально чистими.

Тоді робочий процес для написання та тестування пакету стає приблизно таким.

  1. Збережіть зміни в Packagename.m.
  2. Іди PackagenameTest.nbі роби CTRL + ALT + i.

Це змушує клітини ініціалізації перезавантажувати пакет, що робить тестування справді простим.


1

Наступна функція format[expr_]може використовуватися для відступу / форматування неформатованих mathematicaвиразів, що охоплює сторінку

indent[str_String, ob_String, cb_String, delim_String] := 
  Module[{ind, indent, f, tab}, ind = 0; tab = "    ";
   indent[i_, tab_, nl_] := nl <> Nest[tab <> ToString[#] &, "", i];
   f[c_] := (indent[ind, "", " "] <> c <> indent[++ind, tab, "\n"]) /;StringMatchQ[ob, ___ ~~ c ~~ ___];
   f[c_] := (indent[--ind, "", " "] <> c <> indent[ind, tab, "\n"]) /;StringMatchQ[cb, ___ ~~ c ~~ ___];
   f[c_] := (c <> indent[ind, tab, "\n"]) /;StringMatchQ[delim, ___ ~~ c ~~ ___];
   f[c_] := c;
   f /@ Characters@str // StringJoin];
format[expr_] := indent[expr // InputForm // ToString, "[({", "])}", ";"];

(*    
format[Hold@Module[{ind, indent, f, tab}, ind = 0; tab = "    ";
 indent[i_, tab_, nl_] := nl <> Nest[tab <> ToString[#] &, "", i];
 f[c_] := (indent[ind, "", " "] <> c <> indent[++ind, tab, "\n"]) /;StringMatchQ[ob, ___ ~~ c ~~ ___];
 f[c_] := (indent[--ind, "", " "] <> c <> indent[ind, tab, "\n"]) /;StringMatchQ[cb, ___ ~~ c ~~ ___];
 f[c_] := (c <> indent[ind, tab, "\n"]) /;StringMatchQ[delim, ___ ~~ c ~~ ___];
 f[c_] := c;
 f /@ Characters@str // StringJoin]]
*)

посилання: /codegolf/3088/indent-a-string-using-given-parentheses


Для чого ви використовуєте це на практиці? Вихід є надто "смішним", щоб його можна було прочитати або при застосуванні до вашого коду, або до даних (списків format@RandomInteger[10,{3,3}]): pastebin.com/nUT54Emq Оскільки ви вже маєте основи і вас це цікавить, чи можете ви покращити код до створити корисно читабельне форматування? Тоді наступним кроком буде створення кнопки вставки, яка створить вхідну комірку з непогано відрезаним кодом Mathematica (бажано із збереженням коментарів !!) Дивіться також моє відповідне питання .
Szabolcs
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.