Mathematica:什么是符号编程?
我是斯蒂芬沃尔夫勒姆的忠实粉丝,但他绝对不会羞于表达自己的号角。 在许多参考文献中,他将Mathematica作为一种不同的符号编程范例来颂扬。 我不是Mathematica用户。
我的问题是:这是什么符号编程? 它与功能语言(如Haskell)相比如何?
您可以将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的默认评估程序(以利用Integrate
, Solve
等)并且想要更改评估的默认顺序时,当前Mathematica版本的一个缺点就出现了。 这是可能的,但是很复杂,我想,未来的符号编程实现将会有一个更优雅的控制评估顺序的方式
当我听到“符号编程”这个短语时,LISP,Prolog和(是)Mathematica立刻就想起了。 我将一个符号编程环境描述为用于表示程序文本的表达式恰好也是主要的数据结构。 因此,抽象化抽象变得非常容易,因为数据可以很容易地转化为代码,反之亦然。
Mathematica大量使用这种功能。 甚至比LISP和Prolog(恕我直言)更重。
作为符号编程的例子,请考虑以下事件序列。 我有一个如下所示的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所概括)之间存在区别。 我认为,如果一个人同时学习,那么学习远远超过学习一个 - 独特性的终极考验。
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]
没关系)。
正如其他人在这里已经提到的那样,Mathematica做了很多术语重写。 也许Haskell并不是最好的比较,但Pure是一种很好的功能性词语重写语言(对于Haskell背景的人应该会感到熟悉)。 也许在定期重写时阅读他们的Wiki页面会为你清除一些事情:
http://code.google.com/p/pure-lang/wiki/Rewriting
链接地址: http://www.djcxy.com/p/35543.html