您的Mathematica工具箱里有什么?

159
我们都知道Mathematica非常棒,但它经常缺乏关键功能。你使用哪些外部软件包/工具/资源来与Mathematica一起使用? 我会编辑(并邀请其他人也这样做)这个主帖,以包括专注于科学研究的通用性资源,并且尽可能多的人会发现它们有用。随意贡献任何东西,甚至是小的代码片段(正如我在下面为计时例程所做的那样)。 此外,请包含简短的描述或评论,说明某些东西的优点或提供的实用程序。如果您使用带有附属链接的Amazon图书,请在链接后面提到它,例如通过在链接后面放置您的姓名。

程序包:

  1. LevelScheme是一个扩展Mathematica能力的包,可以生成漂亮的图形。我使用它,不仅因为它可以更好地控制框架/轴刻度,而且因为其最新版本称为SciDraw,将在今年发布。
  2. David Park的Presentation Package(50美元-更新免费)
  3. Jeremy Michelson的grassmannOps包提供了使用Grassmann变量和算子进行代数和微积分运算的资源,这些变量和算子具有非平凡的对易关系。
  4. John Brown的GrassmannAlgebra包和书籍用于处理Grassmann和Clifford代数。
  5. RISC(符号计算研究所)有多个可供下载的Mathematica(和其他语言)包。特别是,有Theorema用于自动定理证明,以及在Algorithmic Combinatorics group's software page上的多个包,用于符号求和、差分方程等。

工具:

  1. MASH 是 Daniel Reeves 出色的 Perl 脚本,基本上为 Mathematica v7 提供了脚本支持。 (现在已经作为 Mathematica 8 的内置功能,使用 -script 选项.)
  2. 一款带有 GNU readline 输入的备用 Mathematica shell (仅限于使用 python 的 *nix 系统)
  3. ColourMaths 包允许您直观地选择表达式的部分并对其进行操作。 http://www.dbaileyconsultancy.co.uk/colour_maths/colour_maths.html

资源:

  1. 沃尔夫拉姆自己的存储库 MathSource 有很多有用但比较狭窄的笔记本,适用于各种应用。还要查看其他部分,例如:

  2. Mathematica Wikibook

书籍:

  1. 如果你想在Mathematica中做更多的事情而不仅仅是使用For循环,那么Leonid Shifrin的《Mathematica programming: an advanced introduction》(webpdf)是必读的。我们很荣幸有Leonid本人在这里回答问题。
  2. James F. Feagin的《Quantum Methods with Mathematica》(amazon
  3. Stephen Wolfram的《The Mathematica Book》(amazon)(web
  4. Schaum's Outline(amazon
  5. Stan Wagon的《Mathematica in Action》(amazon}})- 600页整洁的示例,适用于Mathematica版本7及以上。可视化技术尤其出色,您可以在作者的Demonstrations Page上看到一些示例。
  6. Richard Gaylord的《Mathematica Programming Fundamentals》(pdf)- 对Mathematica编程的大部分必要知识进行了简明扼要的介绍。
  7. Sal Mangano所著的《Mathematica Cookbook》由O'Reilly于2010年出版,共832页。 - 采用著名的O'Reilly Cookbook风格:问题-解决方案。适用于中级用户。
  8. Martha L. Abell和James P. Braselton所著的《Differential Equations with Mathematica, 3rd Ed. Elsevier 2004 Amsterdam》- 893页。适用于初学者,可以同时学习解决微分方程和Mathematica。

未经记录(或很少记录)的功能:

  1. 如何自定义Mathematica键盘快捷键。请参见this question
  2. 如何检查Mathematica自身函数使用的模式和函数。请参见this answer
  3. 如何在Mathematica中实现GraphPlots的一致大小?请参见this question
  4. 如何使用Mathematica制作文档和演示文稿。请参见this question

2
Mathematica 8已经发布,具有更好的shell脚本集成功能。http://www.wolfram.com/mathematica/new-in-8/mathematica-shell-scripts/ - Joshua Martell
2
+1,支持LevelScheme。虽然有时速度有点慢,但它有一个合理的方法来创建刻度线,而且比“Grid”或类似的工具更容易创建适合期刊的图形布局。 - rcollyer
2
正如Alexey在这个问题的评论中提出的建议https://dev59.com/I1TTa4cB1Zd3GeqPvcev,我在这里提出了Mathematica的标签重命名:http://meta.stackexchange.com/questions/81152/retag-mathematica-to-wr-mathematica-or-something-similar。请看一下,如果您同意,请点赞。我在这里发布是因为这个问题在Mma社区中有很多收藏。 - Dr. belisarius
1
这个问题应该是社区维基的,因为它没有正确答案,更像是一个列表。我向所有从这个问题中获得声望的人道歉。 - rcollyer
2
这些回答对于这个问题是有建设性的,应该重新开放。 - M.R.
显示剩余9条评论
26个回答

12

我使用过的一个技巧,可以让你模拟大多数内置函数处理错误参数的方式(通过发送消息然后返回整个未计算的表达式),利用了在定义中使用 Condition 时它的奇怪特性。如果 foo 只能接受一个参数:

foo[x_] := x + 1;
expr : foo[___] /; (Message[foo::argx, foo, Length@Unevaluated[expr], 1]; 
                    False) := Null; (* never reached *)
如果您有更复杂的需求,可以将参数验证和消息生成拆分为独立的函数。在使用Condition中进行副作用时,除了生成消息之外,您可以做更复杂的事情,但在我看来,大多数这些方法都属于“不道德的黑客”范畴,如果可能的话应该避免使用。
此外,在“元编程”类别中,如果您有一个Mathematica包(.m)文件,可以使用"HeldExpressions" 元素,以获取文件中所有表达式都包含在HoldComplete中。这比使用基于文本的搜索要容易得多。不幸的是,没有简单的方法来执行相同的操作以获取笔记本中的表达式,但您可以使用类似以下内容的方式获取所有输入表达式:
inputExpressionsFromNotebookFile[nb_String] :=
 Cases[Get[nb],
  Cell[BoxData[boxes_], "Input", ___] :>
   MakeExpression[StripBoxes[boxes], StandardForm],
  Infinity]

最后,您可以利用Module模拟词法闭包来创建引用类型的等效方式。下面是一个简单的堆栈(它使用了一种变体的Condition技巧来处理错误):

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

  StackInstance::empty = "stack is empty";

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

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

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

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

现在你可以以一种不必要地复杂的方式打印列表的元素,顺序为反向!

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

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

1
在包中使用HeldExpressions元素时加1,我之前不知道这个。我通常会将其作为字符串导入,然后使用ToExpression并将HoldComplete作为最后一个参数。关于使用Condition来处理消息——自1994年以来,这一技术已经成为包编写的标准技术。关于通过Module变量实现持久性——我之前在Mathgroup上发表了一篇长篇帖子:http://groups.google.com/group/comp.soft-sys.math.mathematica/browse_thread/thread/5eff6213a0ab5f51(该主题中的第三篇文章),这与此类似,并且链接到了一些非平凡的用例示例。 - Leonid Shifrin
@Leonid Shifrin:我从一位同事那里了解到了Condition的用法,但并没有意识到它是一种标准技术。关于使用Module符号作为引用类型的链接很有趣! - Pillsy
+1,我从未想过那个。我越了解这种语言,它似乎就越强大。 - rcollyer
@Pillsy 那种方式实现栈的目的是什么? - Mr.Wizard
@Mr.Wizard:我只是选择了我能想到的最简单的可变数据结构来说明这种技术。 - Pillsy

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).   *)

有关“each”函数,请参见https://dev59.com/CnVC5IYBdhLWcg3w21Mq。 - dreeves

12

打印不带上下文前缀的系统符号定义

以下函数contextFreeDefinition[]将尝试打印一个符号的定义,而不带上最常见的上下文前缀。然后可以将其复制到Workbench并进行格式化以提高可读性(选择它,右键单击,源代码-> 格式)。

Clear[commonestContexts, contextFreeDefinition]

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

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

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

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

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

withRules[]

注意:该函数不像WithModule一样本地化变量,这意味着嵌套的本地化结构将无法按预期工作。withRules[{a -> 1, b -> 2}, With[{a=3}, b_ :> b]] 替换嵌套WithRule中的ab,而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]]

以下是一个例子:

Mathematica graphics Mathematica graphics

请注意,较大的n值或图像尺寸容易暴露出图形驱动程序的错误或引入伪影。


Notebook差异功能

Notebook差异功能可在<<AuthorTools`包和(至少在版本8中)未公开的NotebookTools`上下文中使用。这是一个小型GUI,用于比较当前打开的两个笔记本:

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

Mathematica图形


所有这些都很好,但这并没有真正“本地化”变量,因为您可以通过在示例调用之前分配a = 3; b = 4;来看到,然后调用withRules。您可以通过改用以下方式来保存它:SetAttributes [withRules,HoldAll]; withRules [rules_,expr_]:= Unevaluated [expr] /. Unevaluated [rules]。然后与With的语义差异如下:1.规则的右侧现在不会被评估2.withRules不会像With一样解决内部作用域结构的命名冲突。最后一个问题非常严重-具体情况而定,可能是好事或坏事。 - Leonid Shifrin
@Leonid,你说得完全正确,似乎我从来没有在发布代码之前仔细检查过...当我使用这个时,我实际上从来没有给变量赋值,但这是一个非常严重的问题,你是对的。你觉得改正后的版本怎么样?(我并不在意不处理嵌套的 With。这在内置的本地化结构中也不总是有效,例如 With[{a=1}, Block[{a=2}, a]]。你认为为什么嵌套的 Block 在那里不进行本地化,像嵌套的 WithModule 一样?) - Szabolcs
@Leonid 我没有简单地使用 Unevaluated[rules],因为我希望 x -> 1+1 评估 RHS。 - Szabolcs
@Leonid,你说得很对,嵌套本地化问题可能非常严重。我认为嵌套的 With 很容易被发现和避免,但是模式不是:With[{a = 1}, a_ -> a] 会将内部的 a 本地化,而 withRules 则不会。你知道是否有任何方法可以访问Mathematica的内部本地化机制并创建新的构造(类似于 Rule),这些构造也会进行本地化吗?我可能会稍后删除此答案,因为它比有用更危险,但我想先玩一下。 - Szabolcs
本地化,但With不会创建任何新的符号。然而,我不确定设计它的人是否仔细研究了语言的这一方面(可能是这样)。您的最后一条评论:不,请不要删除此答案,它非常有趣!至于本地化机制-在某种程度上是的。我们无法直接访问内置的本地化机制,但我们可以自己编写,以实现本地化。可惜我现在没有更多时间,但我已经做过几次这样的事情。当可能时,我仍然发现重用内置机制的部分更容易。希望我们能很快再次讨论这个问题。 - Leonid Shifrin
显示剩余2条评论

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,使得这种构造变得更加晦涩和有吸引力)。该函数是Fred Simons、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>>}}

它肯定比内置的慢,但对于代码大小来说,仍然相当快,我个人认为。

另一个例子:这里是基于链表和递归纯函数的 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 是的,这很酷。你和Fred做了大部分工作,Bobby和我只添加了一些细节,如果我没记错的话。 - Leonid Shifrin

8
可以使用未记录的命令行选项-batchinput-batchoutput在批处理模式下运行MathKernel:
math -batchinput -batchoutput < input.m > outputfile.txt

(其中input.m是以换行符结尾的批处理输入文件,outputfile.txt是将输出重定向到的文件)。

Mathematica v. >=6中,MathKernel有一个未记录的命令行选项:

-noicon

该选项控制MathKernel是否在任务栏上显示可见图标(至少在Windows下)。

前端界面(至少从v.5开始)具有未记录的命令行选项

-b

这个选项可以禁用启动画面,并使 Mathematica 前端运行更快。

另外还有一个选项。

-directlaunch

该方法禁用启动最近安装的Mathematica版本的机制,而不是启动与系统注册表中的.nb文件相关联的版本。

另一种方法可能

不要启动安装目录中的Mathematica.exe二进制文件,而是启动SystemFiles\FrontEnd\Binaries\Windows中的Mathematica.exe二进制文件。前者是一个简单的启动程序,尽最大努力将打开笔记本的请求重定向到正在运行的用户界面副本。后者是用户界面二进制文件本身。

结合设置全局前端选项VersionedPreferences->True,使用最后一个命令行选项非常方便,它禁用了在安装不同Mathematica版本之间共享首选项:

SetOptions[$FrontEnd, VersionedPreferences -> True]

(以上应在安装的最新版本的Mathematica中进行评估。)

Mathematica 8中,可以在“首选项”对话框中,在“系统”窗格下的“创建和维护特定于版本的前端首选项”设置中进行控制

可以使用未记录的键-h(Windows的代码)获取FrontEnd命令行选项的不完整列表:

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

给出:

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

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

MathKernel和FrontEnd还有其他可能有用的命令行选项吗?如果您知道,请分享。

相关问题


"匹配比特位数?" 这是什么意思? - Mr.Wizard
@Mr.Wizard 可能这个选项只有在64位系统中与 -32 选项结合使用才有意义,它意味着 FrontEnd 使用的 MathKernel 的位数将与操作系统的位数匹配(64位)。在其他情况下,似乎这个选项不会改变任何东西。 - Alexey Popkov

8
这是来自Stan Wagon的书中的配方...当内置的Plot由于精度不足而表现异常时使用它。
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]]]]];

我经常使用Kristjan Kannike的以下技巧,当我需要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]]

一个使用示例会很棒。如果您有时间,请尝试发布一个。 - Dr. belisarius
你认识Kristjan吗?我曾经和他在赫尔辛基的同一组工作过。不错的家伙,世界真小。 - Timo
不行,我在网上找到了他的代码。实际上,我试图给他发电子邮件修复代码中的一个小错误,但他网页上的电子邮件已经失效了。 - Yaroslav Bulatov

7

无限PageWidth的PutAppend

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中引入了一个bug(在版本11.3中已修复):SetOptions不再影响OpenWriteOpenAppend的行为。

一种解决方法是使用显式的PageWidth -> Infinity选项实现自己的PutAppend版本:

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

请注意,我们也可以通过如答案中所示的WriteString来实现它,但在这种情况下,需要预先将表达式转换为相应的InputForm,即ToString[expr, InputForm]

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[]时,它会创建一个私有上下文并加载我的标准包。现在让我们创建一个命令,可以打开一个新的草稿本,具有自己的私有上下文(这样你就可以在那里放肆地hack而不用担心破坏定义),但可以访问您当前的上下文。
SpawnScratch[] := SelfDestruct@Module[{nb,boxExpr,strList},
    strList = Riffle[("\"" <> # <> "\"") & /@ $ContextPath, ","];
    boxExpr = RowBox[{"MyPrivatize", "[",
        RowBox[{"{", RowBox[strList], "}"}], "]"}];
    nb = CreateDocument[];
    writeAndEval[nb,boxExpr];
]

这很酷的一点是由于SelfDestruct,当命令运行时它不会在当前笔记本中留下任何痕迹 - 这很好,因为否则它只会创建杂乱无章的东西。
为了获得额外的风格分,您可以使用InputAutoReplacements为这些宏创建关键字触发器,但我将把这留给读者作为练习。

6

我刚刚在浏览我的一个软件包以便将其包含进来时,发现了一些我定义的消息,这些消息非常有用: Debug::<一些名称>。默认情况下,它们是关闭的,所以不会产生太多额外的开销。但是,如果我需要弄清楚代码的某个部分行为如何,我可以在代码中随意添加它们并打开它们。


自从1991年发布2.0版本以来,Debug已被Trace所取代。 - Dr. belisarius
1
@belisarius,你误解了。它既不是Debug函数也不是Trace函数;它是一组我创建的消息,我可以随时在代码中添加或取消。它们以单词Debug为前缀,就像usage消息以函数名为前缀一样。它提供了与在C++代码中放置一堆cout语句相同的功能。 - rcollyer
1
哦...抱歉。我有点困惑,因为我没有毕业于幼儿园,所以没有学过“大写字母是用来表示国家名称的” :D - Dr. belisarius

6

在内置的作用域构造中,让我感到困扰的一件事是它们会一次性地评估所有本地变量定义,因此你不能写出例如:

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

前一段时间,我想出了一个叫做WithNest的宏,可以让你这样做。我发现它很方便,因为它可以让你在不必像这样做的情况下保持变量绑定的局部性:

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

最终,我找到的最好方法是使用一个特殊符号,使得在绑定列表上进行递归更加容易,并将定义放入自己的包中以保持该符号的隐藏。也许有人对此问题有更简单的解决方案吗?
如果您想尝试,请将以下内容放入名为Scoping.m的文件中:
BeginPackage["Scoping`"];

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

Begin["`Private`"];

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

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

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

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

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

End[];

EndPackage[];

Janus发布了这个版本,并引用了您在MathGroup上的问题:http://stackoverflow.com/questions/4190845/custom-notation-question/4191096#4191096 - Mr.Wizard
谢谢你指出来!我已经有一段时间没有看这些东西了,看到其他的方法很有趣。 - DGrady

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接