Mathematica:什么是符号编程?
我是斯蒂芬·沃尔夫勒姆(Stephen Wolfram)的忠实粉丝,但是他绝对不会害羞。 在许多参考文献中,他将Mathematica作为一种不同的符号编程范例来颂扬。 我不是Mathematica用户。
我的问题是:这是什么符号编程? 它和function语言(如Haskell)相比如何?
您可以将Mathematica的符号编程视为一个search和replace系统,您可以通过指定search和replace规则进行编程。
例如,你可以指定下面的规则
area := Pi*radius^2;
下一次使用area
,将被replace为Pi*radius^2
。 现在,假设你定义了新的规则
radius:=5
现在,无论何时使用radius
,它都会被重写为5
。 如果你评估area
它会被重写成Pi*radius^2
,这会触发radius
重写规则,你会得到Pi*5^2
作为中间结果。 这个新的表单将触发一个内置的^
操作的重写规则,所以expression式将被进一步重写成Pi*25
。 此时重写停止,因为没有适用的规则。
您可以使用您的replace规则作为函数来模拟函数式编程。 例如,如果你想定义一个添加的函数,你可以这样做
add[a_,b_]:=a+b
现在, add[x,y]
重写为x+y
。 如果你想添加只适用于数字a,b,你可以做
add[a_?NumericQ, b_?NumericQ] := a + b
现在,使用你的规则add[2,3]
重写为2+3
,然后使用内置的规则为+
,而add[test1,test2]
保持不变。
这是一个交互式replace规则的例子
a := ChoiceDialog["Pick one", {1, 2, 3, 4}] a+1
在这里, a
被ChoiceDialog
replace,然后被用户在popup的对话框中select的数字取代,这使得数量和触发器的replace规则为+
。 在这里, ChoiceDialog
作为一个内置的replace规则,沿着“将ChoiceDialog [某些东西]replace为用户点击的button的值”。
规则可以使用自己需要通过规则重写来产生True
或False
条件来定义。 例如,假设你发明了一个新的方程求解方法,但是你认为只有当你的方法的最终结果是正面的时候才能起作用。 你可以做下面的规则
solve[x + 5 == b_] := (result = b - 5; result /; result > 0)
在这里, solve[x+5==20]
被replace为15,但solve[x + 5 == -20]
没有改变,因为没有适用的规则。 防止此规则应用的条件是/;result>0
。 评估者从本质上看待规则应用程序的潜在输出,以决定是否继续使用它。
Mathematica的评估者贪婪地重写每个模式,并使用适用于该符号的规则之一。 有时你想要更好的控制,在这种情况下,你可以定义自己的规则,并像这样手动应用它们
myrules={area->Pi radius^2,radius->5} area//.myrules
这将应用myrules
定义的规则,直到结果停止更改。 这与默认评估器非常相似,但是现在可以有多组规则并select性地应用它们。 一个更高级的例子展示了如何创build一个类似Prolog的评估器来search规则应用程序的序列。
当您需要使用Mathematica的默认评估程序(以利用Integrate
, Solve
等) 并且想要改变评估的默认顺序时,Mathematica版本的一个缺点就出现了。 这是可能的,但是复杂的 ,我想,未来的符号编程实现将会有一个更优雅的方式来控制评估序列
当我听到“符号编程”这个短语的时候,LISP,Prolog和(是)Mathematica立即想起来了。 我将描述一个象征性的编程环境,其中用来表示程序文本的expression式也恰好是主要的数据结构。 因此,在抽象上构build抽象变得非常容易,因为数据可以很容易地转换成代码,反之亦然。
Mathematica大量使用这个function。 甚至比LISP和Prolog(恕我直言)更重。
作为符号编程的例子,请考虑以下事件序列。 我有一个CSV文件,看起来像这样:
r,1,2 g,3,4
我读了这个文件:
Import["somefile.csv"] --> {{r,1,2},{g,3,4}}
是结果数据还是代码? 这是两个。 它是从读取文件得到的数据,但它也恰好是构build该数据的expression式。 随着代码的发展,这个expression式是惰性的,因为评估它的结果本身就是简单的。
所以现在我对结果应用一个转换:
% /. {c_, x_, y_} :> {c, Disk[{x, y}]} --> {{r,Disk[{1,2}]},{g,Disk[{3,4}]}}
没有深入细节,所发生的一切就是Disk[{...}]
已经绕过每个input行的最后两个数字。 结果仍然是数据/代码,但仍然是惰性的。 另一个转变:
% /. {"r" -> Red, "g" -> Green} --> {{Red,Disk[{1,2}]},{Green,Disk[{3,4}]}}
是的,仍然是惰性的。 但是,最后一个结果恰巧是Mathematica内置的特定graphics语言的有效指令列表。 最后一个转变,事情开始发生:
% /. x_ :> Graphics[x] --> Graphics[{{Red,Disk[{1,2}]},{Green,Disk[{3,4}]}}]
其实,你不会看到最后的结果。 在一个句法糖的史诗般的展示中,Mathematica会展示这幅红色和绿色圆圈的图片:
但乐趣并不止于此。 在所有的语法糖之下,我们仍然有一个象征性的expression。 我可以应用另一个转换规则:
% /. Red -> Black
普雷斯托! 红圈变黑了。
象征性的编程就是这种“象征推动”。 绝大多数的Mathematica编程是这种性质的。
function与符号
我不会详细讨论符号和函数式编程之间的区别,但我会提供一些意见。
人们可以将符号编程视为对这个问题的回答:“如果我只用expression式转换来模拟所有的东西,会发生什么? 相反,函数式编程可以被看作是一个答案:“如果我只用函数来模拟所有的东西,会发生什么? 就像符号编程一样,函数式编程使得快速build立抽象层很容易。 我在这里给出的例子可以很容易地被复制,例如,Haskell使用function性反应animation方法。 函数式编程全部是关于函数组合,更高级的函数,组合器 – 所有你可以用函数做的漂亮的事情。
Mathematica显然是符号编程的最佳select。 用函数式编写代码是可能的,但是Mathematica中的函数特性实际上只是一个转换过程中的薄板(以及一个漏洞的抽象,请参见下面的脚注)。
Haskell显然是function编程优化的。 可以用符号风格编写代码,但是我会质疑程序和数据的语法表示是非常不同的,这使得体验并不理想。
结束语
总之,我主张在函数式编程(如Haskell所概括的)和符号式编程(如Mathematica所概括)之间存在区别。 我认为,如果一个人同时学习,那么学习远远超过学习一个 – 独特性的终极考验。
Mathematica中的function抽象漏洞?
是的,漏水 试试这个,例如:
f[x_] := g[Function[a, x]]; g[fn_] := Module[{h}, h[a_] := fn[a]; h[0]]; f[999]
正式报告并得到世界资源研究所的承认。 响应:避免使用Function[var, body]
( Function[body]
没关系)。
正如其他人在这里已经提到的,Mathematica做了很多术语重写。 也许Haskell并不是最好的比较,但Pure是一个很好的function性词条重写语言(对于Haskell背景的人应该感到熟悉)。 也许阅读他们的维基页面的定期重写将清除你的几件事情:
Mathematica正在大量使用术语重写。 该语言为各种forms的重写提供了特殊的语法,特别支持规则和策略。 这个范式并不是“新”,当然也不是独一无二的,但是它们绝对是这个“象征性的编程”事物的一个极端的边缘,与其他强大的玩家如Axiom一样。
至于与Haskell的比较,那么你可以在那里做一些改动,从废料样板库中获得一些帮助,但是它不像在dynamictypes的Mathematica中那么容易。