Что находится в вашей сумке для инструментов Mathematica? [закрыто]

152

Мы все знаем, что Mathematica великолепна, но ей также часто не хватает критически важной функциональности. Какие внешние пакеты / инструменты / ресурсы вы используете с Mathematica?

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

Также приветствуются недокументированные и полезные функции в Mathematica 7 и более поздних версиях, которые вы нашли или нашли в какой-либо статье / сайте.

Пожалуйста, включите краткое описание или комментарий о том, почему что-то великолепно или какую утилиту он предоставляет. Если вы ссылаетесь на книги на Amazon с помощью партнерских ссылок, укажите это, например, указав свое имя после ссылки.


Пакеты:

  1. LevelSchemeэто пакет, который значительно расширяет возможности Mathematica по созданию хороших участков. Я использую его, если не для чего-то еще, то для намного, намного улучшенного контроля над тиками кадра / осей. Его новейшая версия называется SciDraw, и она будет выпущена где-то в этом году.
  2. David Park's Presentation Package(50 долларов США - бесплатно)
  3. grassmannOpsПакет Джереми Майкельсона предоставляет ресурсы для выполнения алгебры и исчисления с грассмановыми переменными и операторами, которые имеют нетривиальные коммутационные отношения.
  4. GrassmannAlgebraПакет и книга Джона Брауна для работы с алгебрами Грассмана и Клиффорда.
  5. RISC (Научно-исследовательский институт символических вычислений) имеет множество пакетов для Mathematica (и других языков), доступных для скачивания. В частности, есть Теорема для автоматического доказательства теорем и множество пакетов для символьного суммирования, разностных уравнений и т. Д. На странице программного обеспечения группы Algorithmic Combinatorics .

Инструменты:

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

Ресурсы:

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

  2. Mathematica Wikibook .

Книги:

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

Недокументированные (или едва документированные) функции:

  1. Как настроить сочетания клавиш Mathematica. См this question.
  2. Как проверить шаблоны и функции, используемые собственными функциями Mathematica. Видетьthis answer
  3. Как добиться согласованного размера для GraphPlots в Mathematica? См this question.
  4. Как составлять документы и презентации с помощью Mathematica. См this question.
Dr. belisarius
источник
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]

Но с помощью этого и менее полного примера выше легко увидеть, как очень сложные преобразования возможны с минимумом кода.

rcollyer
источник
Оригинальный код Fortran77 был реструктурирован в День благодарения 1996 года и, следовательно, на протяжении многих лет известен как turkey.f ...: D Очень хорошая графика, кстати. Вспомнили мне чудовище Фаликова ...
Доктор Белизарий
@belisarius, я не читал историю, это смешно. Я только начал использовать Wannier90, но это один из наиболее хорошо организованных и хорошо написанных Fortranкодов, которые я видел. Заставляет меня почти подумать об использовании Fortran...
rcollyer
Интересно, не могли бы вы добавить автономный пример SelectEquivalents в действии
Ярослав Булатов
@ Ярослав Булатов, добавил пример, по запросу. Позвольте мне знать, если это помогает. Если этого не произойдет, мы посмотрим, что мы можем сделать.
rcollyer
Вы получите галочку в этом «вопросе» за самый интересный фрагмент кода.
Тимо
57

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

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

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

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

  1. Откройте новый блокнот.
  2. Выберите пункт меню Формат / Редактировать таблицу стилей ...
  3. В диалоговом окне рядом с Введите имя стиля: тип Shell.
  4. Выберите скобку ячейки рядом с новым стилем.
  5. Выберите пункт меню Ячейка / Показать выражение
  6. Замените выражение ячейки текстом шага 6, приведенным ниже.
  7. Еще раз выберите пункт меню Ячейка / Показать выражение
  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 + ВВОД.

Лучше всего хранить этот определенный стиль в центральной таблице стилей. Кроме того, такие функции оценки, как 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[]вычислено, входная ячейка будет удалена и заменена новой входной ячейкой, которая оценивает ее содержимое как команду оболочки.

оборота WReach
источник
3
@WReach +100! Хотел бы я знать это раньше! Это очень полезный материал, по крайней мере для меня. Спасибо, что поделился!
Леонид Шифрин
Это выглядит довольно элегантно! CellEvaluationFunctionЯ думаю, что можно использовать и для взлома синтаксиса низкого уровня.
Mr.Wizard
@Leonid По крайней мере для FrontEnd, CellEvaluationFunctionтот крючок, который вы искали?
Мистер Волшебник
2
Кроме того: есть еще один Cellвариант, связанный с оценкой ячеек Evaluator -> "EvaluatorName". Значение "EvaluatorName"может быть настроено через диалог Evaluation :: Kernel Configuration Options .... Я до сих пор не знаю, возможно ли это настроить программно ... Этот метод позволяет использовать разные MathKernels в разных Cells в одной записной книжке. Эти MathKernels могут быть из разных версий Mathematica .
Алексей Попков
1
@Szabolcs Все мои собственные применения этого метода включают либо подход stdin _ / _ stdout, как показано выше, либо автономный удаленный запрос, такой как запрос SQL или операция HTTP. Вы можете попытаться настроить веб-приложение Python REPL (как это ) и взаимодействовать с ним, используя Import, или, возможно, запустить внешний процесс Python и обмениваться данными через его потоки (например, используя Java ProcessBuilder ). Я уверен, что есть лучший способ Mathematica - звучит как хороший вопрос :)
WReach
36

Тодд Гейли (Wolfram Research) просто прислал мне приятный хак, который позволяет «обернуть» встроенные функции произвольным кодом. Я чувствую, что должен поделиться этим полезным инструментом. Вот ответ Тодда на мой question.

Немного интересной (?) Истории: этот стиль взлома для «обертывания» встроенной функции был придуман Робби Виллегасом и мной в 1994 году, по иронии судьбы, для функции Message, в пакете под названием ErrorHelp, который я написал для журнала Mathematica Journal. тогда С тех пор он много раз использовался многими людьми. Это немного инсайдерский трюк, но я думаю, что будет справедливо сказать, что он стал каноническим способом внедрения вашего собственного кода в определение встроенной функции. Это делает работу хорошо. Конечно, вы можете поместить переменную $ inMsg в любой личный контекст, который вы пожелаете.

Unprotect[Message];

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

Protect[Message];
Алексей Попков
источник
@ Алексей, мне сложно это понять. Не могли бы вы объяснить, как это работает? Разве не должно быть где-нибудь Unprotect [Message]? И не содержит ли этот пример бесконечную рекурсию? И, ! TrueQ [$ inMsg] имеет ли это смысл, если $ inMsg определен внутри блока и не определен вне блока?
Sjoerd C. de Vries
9
@Sjoerd Из того, что я понимаю, Unprotectдействительно, должно быть, просто не учтено . Точка Block(динамическая область видимости) и $inMsgявляется именно для предотвращения бесконечной рекурсии. Поскольку $inMsgне определено снаружи (это важное требование), сначала выполняется TrueQоценка False, и мы входим в тело функции. Но когда у нас есть вызов функции внутри тела, условие оценивается как False(поскольку переменная была переопределена блоком). Таким образом, пользовательское правило не соответствует, и вместо него используется встроенное правило.
Леонид Шифрин
1
@ Леонид Спасибо, теперь я понял. Очень умный!
Sjoerd C. de Vries
1
Я только что обнаружил, что этот метод обсуждался Робби Виллегасом из Wolfram Research на конференции разработчиков в 1999 году. См. Записную книжку «Работа с неоцененными выражениями», размещенную здесь . В этой записной книжке Робби Виллегас обсуждает эту уловку в подразделе «Уловка My Block для перехвата вызовов встроенных функций».
Алексей Попков
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}]

РЕДАКТИРОВАТЬ: Вот версия из 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».
Мистер Волшебник,
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 пунктов ближе к привилегии редактирования для вас.
Мистер Волшебник,
Для меня это выглядит как вариант раннего или позднего или полного отсутствия оценки.
user2432923
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: ``";

Представленная реализация не поддерживает ни up-значения, ни карри, ни шаблоны более общие, чем простое определение функции. Это остается полезным, однако.

оборота WReach
источник
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 post # 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.}  *)
Mr.Wizard
источник
3
+1 Хорошая коллекция! Что касается внешних модификаций изнутри Compile- весь мой пост здесь: stackoverflow.com/questions/5246330/… , должен был продемонстрировать эту возможность в нетривиальной обстановке (там было опубликовано там более короткое и быстрое решение рассматриваемой проблемы) , ИМО, самая большая победа здесь - это возможность эмулировать передачу по ссылке и разбивать большие скомпилированные функции на более управляемые и повторно используемые фрагменты.
Леонид Шифрин
1
Вы можете также настроить информацию синтаксиса Fold и FoldList в новом определении: SyntaxInformation [Fold] = { "ArgumentsPattern" -> {_, . , _}}; SyntaxInformation [FoldList] = {"ArgumentsPattern" -> {_, _., {_ }}};
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) Общая проблема при экспорте в формат EMF состоит в том, что большинство программ (не только Mathematica ) генерируют файл, который выглядит хорошо при размере по умолчанию, но становится уродливым при его увеличении. Это связано с тем, что метафайлы выбираются с точностью разрешения экрана . Качество созданного файла EMF может быть улучшено за счет 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 и увеличите их, вы увидите, что у первого «а» есть пилообразный сигнал, а у второго нет (протестировано с Mathematica 6).

Крис Дегнен ( Chris Degnen)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 отвечает ответчикам (кроме аннотаций ) с использованием SO API .

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

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 прав. Это «Сообщество вики»
доктор Белизарий
@belisarius Я только что скопировал это в ответ на вопрос Бретта ...
Sjoerd C. de Vries
@Sjoerd Ваш сюжет здесь не обновляется автоматически.
Доктор Велизарий
@belisarius На самом деле я надеялся, что вы возьмете на себя это задание ... ;-)
Шурд де Врис
13

Кэширование выражений

Я считаю, что эти функции очень полезны для кэширования любого выражения. Интересная вещь для этих двух функций заключается в том, что само удерживаемое выражение используется в качестве ключа хеш-таблицы / символа Cache или CacheIndex, по сравнению с хорошо известным памятником в mathematica, где вы можете кэшировать результат, только если функция определена как 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}, например, для получения кэшированного результата.

Разновидность этих функций для того, чтобы иметь локализованный кеш (т. Е. Кеш-память автоматически высвобождается за пределами конструкции Block), см. В этом посте. Избегайте повторных обращений к интерполяции.

Удаление кэшированных значений

Чтобы удалить кэшированные значения, когда вы не знаете количество определений функции. Я считаю, что определения имеют пробел где-то в своих аргументах.

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;

Вы можете получить доступ к списку ключей (или свойств) символа, используя эти функции в зависимости от того, что dreeves представили в посте на этом сайте:

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}]
  ];

Наконец, вот простой способ создать символ, который ведет себя как объект в объектно-ориентированном программировании (он просто воспроизводит самое основное поведение ООП, но я нахожу синтаксис элегантным):

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, а методы - как задержанные Upvalues ​​в символе, созданном модулем, который возвращается. Я нашел синтаксис для function2, который является обычным OO-синтаксисом для функций в структуре данных Tree в 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, вы можете определить function и function2 как это вне NewObject (и не внутри) и использовать то же, что и раньше ,

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

Используйте UpValues ​​[тип], чтобы увидеть, что функция и 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 Gather / Collect правильно

Как бы вы сделали функцию сводной таблицы в Mathematica?

Mathematica быстрый алгоритм 2D-биннинга

Internal`Bag

Даниэль Лихтблау описывает здесь интересную внутреннюю структуру данных для растущих списков.

Внедрение Quadtree в Mathematica

Функции отладки

Эти два сообщения указывают на полезные функции для отладки:

Как отлаживать при написании маленьких или больших кодов с помощью Mathematica? верстак? мма отладчик? или что-то другое? (Покажи это)

/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

faysou
источник
Связано: « Лучший способ построить функцию с памятью ». WReach привел удивительный пример простой функции, которая не только запоминает свои значения, но также записывает их в файл и читает при перезапуске в обратном направлении.
Алексей Попков
1
Связанный: " Mathematica: Как очистить кэш для символа, то есть Unset без шаблонов DownValues ". Этот вопрос показывает, как очистить кэш с помощью стандартной памяти f[x_] := f[x] = some code.
Саймон
7
+1 Существует хороший Notational удобство , что избавляет от необходимости повторять левые из определения в функции кэширования, например: c:Cache[expr_] := c = expr.
Посмотрите
Хороший вариант 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).   *)
dreeves
источник
Для «каждой» функции см 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помимо генерации сообщений, но, по моему мнению, большинство из них попадают в категорию «подлых хаков», и их следует избегать, если это возможно.

Кроме того, в категории «метапрограммирование», если у вас есть .mфайл пакета Mathematica ( ), вы можете использовать этот "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]]
Pillsy
источник
1
+1 за HeldExpressionsэлемент в пакетах, не знал об этом. Я обычно импортирования в виде строки , а затем использовать ToExpressionс HoldCompleteкачестве последнего арг. Что касается использования Conditionдля сообщений - это был стандартный метод при написании пакетов, по крайней мере, с 1994 года. Что касается постоянства с помощью Modulevars - я уже давно писал об этом в Mathgroup : groups.google.com/group/comp.soft- sys.math.mathematica /… (мой третий пост в этой теме), в том же духе и содержит ссылки на несколько нетривиальных примеров использования.
Леонид Шифрин
@ Леонид Шифрин: Я взял эту Conditionвещь как знания, вероятно, от коллеги, но не понял, что это стандартная техника. Ссылка на использование Moduleсимволов в качестве ссылочных типов интересна!
Пилси
+1, я никогда не думал об этом. Чем больше я узнаю об этом языке, тем мощнее он кажется.
rcollyer
@Pillsy, какова цель сделать стек таким образом?
Мистер Волшебник
@ Mr.Wizard: Я просто выбрал одну из самых простых изменяемых структур данных, которые я мог придумать, чтобы проиллюстрировать эту технику.
Пилси
11

Распечатка определений символов системы без добавления контекста

contextFreeDefinition[]Ниже функция будет пытаться напечатать определение символа без наиболее общего контекста предварённого. Затем определение можно скопировать в Workbench и отформатировать для удобства чтения (выберите его, щелкните правой кнопкой мыши, Source -> Format)

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]]

справила []

Предостережение: эта функция не локализует переменные таким же образом 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или большой размер изображения имеет тенденцию выставлять ошибки графического драйвера или вносить артефакты.


Функциональность diff ноутбука

Функциональность diff ноутбука доступна в <<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]. Различия по семантике With: 1. Правила теперь не оцениваются 2. withRulesНе разрешает конфликты имен с внутренними контекстными конструкциями, как это Withделается. Последнее довольно серьезно - быть хорошим или плохим в зависимости от случая.
Леонид Шифрин
@ Leonid Вы совершенно правы, кажется, я никогда не учусь проверять код перед публикацией ... когда я использую это, я практически никогда не присваиваю значения переменным, но это довольно серьезная проблема, вы правы. Что вы думаете о исправленной версии? (Меня не волнует, что не нужно обрабатывать вложенные Withs. Это также не всегда работает со встроенными конструкциями локализации, например With[{a=1}, Block[{a=2}, a]]. Как вы думаете, есть ли веская причина, почему вложенные Blockне локализуются там, как вложенные Withи Moduleделают?)
Сабольч
@ Леонид Я не просто использовал, Unevaluated[rules]потому что хотел x -> 1+1оценить RHS.
Сабольч
@ 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, в которых участвуют Фред Саймонс, Szabolcs Horvat, DrMajorBob и все остальные. Пример:

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 Вероятно, эта опция имеет смысл только в 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

Поместить приложение с PageWidth -> Infinity

В 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>. По умолчанию они отключены, поэтому не создают больших накладных расходов. Но я могу засорять свой код ими и включать их, если мне нужно точно выяснить, как немного кода ведет себя.

rcollyer
источник
Из справки> Начиная с версии 2.0 (выпущенной в 1991 году), отладка была заменена Trace.
Доктор Велизарий
1
@belisarius, ты упустил суть. Это не Debugни Traceфункции, ни функции; это набор сообщений, которые я создал, чтобы я мог засорять свой код для включения / выключения по желанию. Они предваряются словом Debug, таким же образом, как usageи название функции, перед MSG. Он обеспечивает ту же функциональность, что и размещение набора coutоператоров в коде c ++.
2010 г.
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[];
DGrady
источник
Янус опубликовал эту версию и сослался на ваш вопрос в 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] аккуратно удаляет цитаты.

Chris Degnen
источник
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[];
Szabolcs
источник
4

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

РЕДАКТИРОВАТЬ

Потерпев некоторое время от этого, я однажды узнал, что можно создать автосохранение из кода 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
Sjoerd C. de Vries
1
@ j0ker5 Попробуйте мой приведенный выше код, и вы увидите, что UpdateInterval не всегда заставляет обновления размещаться с указанным интервалом. Этот код также показывает, что Dynamic работает, только если ячейка, в которой он содержится, видна во внешнем интерфейсе . Это действительно останавливает момент, когда это вне поля зрения. Люди действительно не должны доверять этому коду для сохранения своих файлов, потому что это не так. Это опасно
Sjoerd C. de Vries
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.

Это заставляет ячейки инициализации перезагружать пакет, что делает тестирование очень простым.

оборота nixeagle
источник
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 (желательно с сохранением комментариев !!). См. Также мой связанный вопрос .
Сабольч