我是史蒂芬·沃尔夫拉姆(Stephen Wolfram)的忠实拥护者,但他绝对不会害羞自己的号角。在许多参考文献中,他都称赞Mathematica是一种不同的符号编程范例。我不是Mathematica用户。
我的问题是:这是什么符号编程?它与功能语言(例如Haskell)相比如何?
Answers:
您可以将Mathematica的符号编程视为通过指定搜索和替换规则进行编程的搜索和替换系统。
例如,您可以指定以下规则
area := Pi*radius^2;
下次使用时area
,它将替换为Pi*radius^2
。现在,假设您定义了新规则
radius:=5
现在,无论何时使用radius
,它都会被重写为5
。如果您评估area
它,它将被重写为Pi*radius^2
触发触发器的重写规则,radius
并且您将获得Pi*5^2
一个中间结果。这种新形式将触发用于^
操作的内置重写规则,因此表达式将被进一步重写为Pi*25
。此时,由于没有适用的规则,因此重写停止。
您可以通过将替换规则用作函数来模拟函数式编程。例如,如果要定义一个添加的函数,则可以执行
add[a_,b_]:=a+b
现在add[x,y]
被重写为x+y
。如果您希望加法仅适用于数字a,b,则可以改为
add[a_?NumericQ, b_?NumericQ] := a + b
现在,add[2,3]
被重写为2+3
使用您的规则,然后5
使用内置规则+
,而add[test1,test2]
保持不变。
这是交互式替换规则的示例
a := ChoiceDialog["Pick one", {1, 2, 3, 4}]
a+1
在这里,a
被替换为ChoiceDialog
,然后被替换为用户在弹出的对话框中选择的数字,这使数量成为数字并触发的替换规则+
。在这里,ChoiceDialog
作为一条内置的替换规则,它是“用用户单击的按钮的值替换ChoiceDialog [一些东西]”。
可以使用条件定义规则,规则本身必须经过规则重写才能生成True
或False
。例如,假设您发明了一种新的方程式求解方法,但是您认为它仅在方法的最终结果为正数时才有效。您可以执行以下规则
solve[x + 5 == b_] := (result = b - 5; result /; result > 0)
在这里,solve[x+5==20]
被替换为15,但是solve[x + 5 == -20]
没有变化,因为没有适用的规则。阻止该规则应用的条件是/;result>0
。评估程序本质上是看规则应用程序的潜在输出来决定是否继续执行。
Mathematica的评估者贪婪地用适用于该符号的规则之一重写每个模式。有时您想要更好的控制,在这种情况下,您可以定义自己的规则并像这样手动应用它们
myrules={area->Pi radius^2,radius->5}
area//.myrules
这将应用定义的规则,myrules
直到结果停止更改。这与默认评估程序非常相似,但是现在您可以拥有几组规则并有选择地应用它们。一个更高级的示例显示了如何制作类似于Prolog的评估程序,该评估程序搜索规则应用程序的序列。
当你需要使用Mathematica的默认评估(以利用当前的Mathematica版本的一个缺点大作Integrate
,Solve
等),以及要评价的改变默认顺序。这是可能的,但很复杂,我想认为符号编程的某些将来实现将有一种更优雅的方式来控制求值顺序
Solve
只是另一套重写规则。当你给一些方程是数学不能解决Solve[hard_equations]
化石是Solve[hard_equations]
,你可以定义自定义Solve
适用于这种情况下的规则。在这种情况下,我猜他们使用/;。有条件为“可以用Mathematica中的方法解决的任何方程式”定义模式,因此对于硬方程式,内置规则不适用,而是Solve
保持原始形式
当我听到“符号编程”一词时,LISP,Prolog和(是)Mathematica立刻浮现在脑海。我将符号编程环境描述为一种环境,其中用于表示程序文本的表达式也恰好是主要的数据结构。结果,由于可以轻松地将数据转换为代码,反之亦然,因此在抽象之上构建抽象变得非常容易。
Mathematica大量利用此功能。比LISP和Prolog(IMHO)重得多。
作为符号编程的示例,请考虑以下事件序列。我有一个CSV文件,看起来像这样:
r,1,2
g,3,4
我在以下位置读取了该文件:
Import["somefile.csv"]
--> {{r,1,2},{g,3,4}}
结果数据或代码?两者都是。数据是读取文件所产生的,但也恰好是构造该数据的表达式。但是,随着代码的发展,该表达式是惰性的,因为对其求值的结果只是其本身。
所以现在我将转换应用于结果:
% /. {c_, x_, y_} :> {c, Disk[{x, y}]}
--> {{r,Disk[{1,2}]},{g,Disk[{3,4}]}}
如果不赘述细节,那么所发生的一切就是将Disk[{...}]
每个输入行的最后两个数字包裹起来。结果仍然是数据/代码,但仍然是惰性的。另一变换:
% /. {"r" -> Red, "g" -> Green}
--> {{Red,Disk[{1,2}]},{Green,Disk[{3,4}]}}
是的,仍然是惰性的。但是,碰巧的是,最后的结果恰好是Mathematica内置的特定于图形的领域语言中的有效指令列表。最后的转变,事情开始发生:
% /. x_ :> Graphics[x]
--> Graphics[{{Red,Disk[{1,2}]},{Green,Disk[{3,4}]}}]
实际上,您不会看到最后的结果。在史诗般的语法糖展示中,Mathematica会显示这张红色和绿色圆圈的图片:
但是乐趣不止于此。在所有语法糖的下面,我们仍然具有象征性的表达。我可以应用另一个转换规则:
% /. Red -> Black
快点!红色圆圈变成黑色。
正是这种“符号推送”是符号编程的特征。大多数Mathematica编程都是这种性质的。
我不会详细讨论符号编程和函数编程之间的区别,但我会提供一些意见。
可以将符号编程视为对以下问题的答案:“如果仅尝试使用表达式转换对所有内容进行建模,将会发生什么?” 相比之下,函数式编程可以看作是对以下问题的答案:“如果我尝试仅使用函数对所有模型进行建模,将会发生什么?” 就像符号编程一样,函数式编程可以轻松快速地建立抽象层。我在这里给出的示例可以很容易地使用功能反应动画方法在Haskell中重现。函数式编程主要涉及函数组合,高级函数,组合器-函数可以完成的所有精美操作。
Mathematica显然已针对符号编程进行了优化。可以以函数样式编写代码,但是Mathematica中的函数特征实际上只是转换的薄薄表皮(以及那儿的抽象漏洞,请参见下面的脚注)。
Haskell显然已针对函数式编程进行了优化。可以用符号样式编写代码,但是我会怀疑程序和数据的语法表示形式是截然不同的,从而使体验不够理想。
总之,我主张函数式编程(如Haskell所概括)和符号编程(如Mathematica所概括)之间是有区别的。我认为,如果一个人同时学习,那么一个人将比学习一个人学到的东西要多得多。
是的,漏水。试试这个,例如:
f[x_] := g[Function[a, x]];
g[fn_] := Module[{h}, h[a_] := fn[a]; h[0]];
f[999]
向WRI正式报告并得到其认可。响应:避免使用Function[var, body]
(Function[body]
可以)。
Function[var, body]
吗?这很奇怪,因为它是文档中建议的...
Function
可以治愈的,甚至在原则上-至少在当前侵入语义Rule
和RuleDelayed
,不尊重内心的作用域结构的绑定,包括他们自己。这种现象在我看来,更关系到这个属性Rule
和RuleDelayed
,比专门Function
。但是无论哪种方式,我都同意现在改变这一点非常危险。太糟糕了,因为Function[var,body]
不应该使用-此类错误几乎不可能在大型项目中发现。
正如这里其他人已经提到的,Mathematica进行了大量的术语重写。虽然也许Haskell不是最好的比较,但是Pure是一种很好的功能术语重写语言(对于具有Haskell背景的人应该很熟悉)。也许阅读有关术语重写的Wiki页面将为您清除一些注意事项:
符号不应该与功能进行对比,它应该与数值编程进行对比。以MatLab与Mathematica为例。假设我想要一个矩阵的特征多项式。如果我想在Mathematica中做到这一点,可以将单位矩阵(I)和矩阵(A)本身放入Mathematica,然后执行以下操作:
Det[A-lambda*I]
而且我会得到特征多项式(没关系,可能有一个特征多项式函数),另一方面,如果我在MatLab中,我将无法使用基础MatLab进行分析,因为基础MatLab(没关系,可能有特征多项式函数)仅擅长计算有限精度数,而不是其中存在随机lambda(我们的符号)的事物。您要做的就是购买附加的Symbolab,然后将lambda定义为其自己的代码行,然后将其写出(其中它将A矩阵转换为有理数矩阵,而不是有限精度的小数) ,虽然在这样的小情况下性能差异可能不会引起注意,但就相对速度而言,它可能会比Mathematica慢得多。
因此,区别在于,符号语言对以完美的精度进行计算很感兴趣(通常使用有理数而不是数值),而数字编程语言则非常适合您需要进行的绝大多数计算,并且它们倾向于在它们所要执行的数值运算上要更快(MatLab在这方面对于高级语言几乎是无与伦比的-不包括C ++等),而在符号运算上则很糟糕。