Ответ 1
Отладчики обычно более полезны, когда вы программируете в стиле stateful (переменные, назначения и т.д.) - по крайней мере, это был мой опыт. Для идиоматического программирования Mathematica (функционально-основанного на правилах) некоторые версии операторов Print
не менее эффективны. Вы можете посмотреть эту статью для некоторых вариантов утилиты отладки печати. Я запишу свою версию, взятую из this Сообщение Mathgroup.
SetAttributes[ShowIt, HoldAll];
ShowIt[code_] :=
Module[{y},
Print[ToString[Unevaluated[code]], " = ", y = code];
y];
Идея состоит в том, что вы можете вставить такой вызов функции в "трубу" вызовов функций - он печатает значение, а затем передает его следующей (окружной) функции. В качестве простого примера:
In[29]:= Map[#^2&,[email protected][Range[10],EvenQ]]
During evaluation of In[29]:= Select[Range[10], EvenQ] = {2,4,6,8,10}
Out[29]= {4,16,36,64,100}
Это должно работать нормально в большинстве случаев (за исключением, возможно, тех, где окружающая функция содержит свои аргументы и действует на них нетривиально). Одной из причин, по которой этот подход очень эффективен в Mathematica, является то, что функциональное программирование приводит к программам, в которых (почти) каждая часть имеет смысл сама по себе - поскольку результат одной функции обычно передается непосредственно в прилагаемую функцию.
Тем не менее, вы можете использовать отладчик, как в интерактивном сеансе, так и в WorkBench, используя режим "Debug As Mathematica". В то время как я сам использую WorkBench сам, я никогда не нашел это необходимым, но YMMV.
Другим замечательным средством, которое помогает много, является встроенная команда Trace. Я рекомендую прочитать документацию на нем - он имеет ряд дополнительных опций и может быть настроен, чтобы помочь многим. Я приведу один простой, но нетривиальный пример: отслеживание выполнения алгоритма mergesort со следующей (упрощенной) реализацией:
Clear[merge];
merge[{}, {x__}] := {x};
merge[{x__}, {}] := {x}
merge[{x__?NumericQ}, {y__?NumericQ}] /; First[{x}] <= First[{y}] :=
Flatten[{First[{x}], merge[Rest[{x}], {y}]}];
merge[{x__?NumericQ}, {y__?NumericQ}] := merge[{y}, {x}];
Clear[mergesort];
mergesort[x : {} | {_}] := x;
mergesort[x : {__?NumericQ}] :=
With[{splitlen = IntegerPart[Length[x]/2]},
merge[mergesort[Take[x, splitlen]], mergesort[Drop[x, splitlen]]]]
Мы возьмем очень маленький список ввода, просто чтобы уменьшить длину вывода:
In[41]:= testlst = RandomInteger[10, 5]
Out[41]= {0, 6, 9, 8, 8}
Вы можете просто использовать Trace[mergesort[testlst]];
, но вывод не очень легко читать, так как он содержит все этапы. Используя
In[42]:= Trace[mergesort[testlst],_mergesort]
Out[42]= {mergesort[{0,6,9,8,8}],{mergesort[{0,6}],{mergesort[{0}]},
{mergesort[{6}]}},{mergesort[{9,8,8}],{mergesort[{9}]},{mergesort[{8,8}],
{mergesort[{8}]},{mergesort[{8}]}}}}
Вы получаете очень четкую картину рекурсивных вызовов функций. Вы можете пойти глубже и проследить динамику функции merge
. Для этого вам нужно обработать результат Trace
(который также является выражением Mathematica!):
In[43]:=
Cases[Trace[mergesort[testlst],_merge],merge[x__List]/;FreeQ[{x},mergesort]:>
HoldForm[merge[x]],Infinity]
Out[43]= {merge[{0},{6}],merge[{},{6}],merge[{8},{8}],merge[{},{8}],
merge[{9},{8,8}],merge[{8,8},{9}],merge[{8},{9}],merge[{},{9}],merge[{0,6},
{8,8,9}],merge[{6},{8,8,9}],merge[{},{8,8,9}]}
Этот последний пример иллюстрирует, что даже когда трудно сконфигурировать Trace
, чтобы отфильтровывать нежелательные этапы выполнения, можно просто выполнить пост-обработку результатов Trace
, используя стандартные средства, которые Mathematica обеспечивает для деструктурирования выражения (например, как Cases
).
Позвольте мне также упомянуть, что пользователь и консультант Mathematica Дэвид Бэйли написал пакет DebugTrace, который должен быть альтернативой отладчик. У меня еще не было возможности попробовать, но я уверен, что стоит попробовать.
Наконец, хотя это не связано напрямую с отладкой, WorkBench имеет интегрированную модульную систему тестирования MUnit, которую я нашел очень полезной. Он аналогичен по духу хорошо известным модульным модулям тестирования на других языках, таких как JUnit для Java. Для крупномасштабного развития это может быть реальной помощью.
Что касается использования WorkBench, я бы сказал, что он действительно рассчитывает использовать его для чего угодно, кроме самых маленьких проектов (или даже для них). Он основан на Eclipse, и вы получаете такие же приятные вещи, как редактор с подсветкой кода, "перейти к определению функции", навигации, поиска, интеграции CVS/SVN и т.д. В то же время вы не теряют почти что-либо с точки зрения интерактивности - вы все же можете развить новую функциональность в интерактивной сессии Mathematica, связанной с WorkBench при работе в режиме "Run as Mathematica". Для больших проектов, связанных с множеством пакетов, я просто не вижу причин не использовать его.