我是斯蒂芬·沃尔夫勒姆(Stephen Wolfram)的忠实粉丝,但他绝对不会羞辱自己的号角。在许多参考文献中,他将Mathematica称为不同的符号编程范例。我不是Mathematica用户。
我的问题是:这个符号编程是什么?它与函数式语言(如Haskell)相比如何?
答案 0 :(得分:73)
当我听到“符号编程”这个短语时,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
的Presto!红圈变黑了。
正是这种“符号推动”是象征性编程的特征。绝大多数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]
Duly向WRI报告并承认。回复:避免使用Function[var, body]
(Function[body]
没问题。)
答案 1 :(得分:72)
您可以将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
,然后被替换为用户在弹出的对话框中选择的数字,这使得两个数量都为数字并触发{{1}的替换规则}。在这里,+
作为内置替换规则,沿着“将ChoiceDialog [some stuff]替换为用户点击的按钮值”。
可以使用自己需要通过规则重写的条件来定义规则,以便生成ChoiceDialog
或True
。例如,假设您发明了一种新的方程求解方法,但您认为只有在方法的最终结果为正时才有效。您可以执行以下规则
False
此处, solve[x + 5 == b_] := (result = b - 5; result /; result > 0)
被替换为15,但solve[x+5==20]
未更改,因为没有适用的规则。阻止此规则应用的条件是solve[x + 5 == -20]
。 Evaluator主要查看规则应用程序的潜在输出,以决定是否继续使用它。
Mathematica的评估员贪婪地用一个适用于该符号的规则重写每个模式。有时你想要更好的控制,在这种情况下你可以定义自己的规则并像这样手动应用它们
/;result>0
这将应用myrules={area->Pi radius^2,radius->5}
area//.myrules
中定义的规则,直到结果停止更改。这与默认评估器非常相似,但现在您可以拥有多组规则并有选择地应用它们。更高级的example显示了如何使类似Prolog的评估器搜索规则应用程序序列。
当您需要使用Mathematica的默认评估程序(使用myrules
,Integrate
等)时,会出现当前Mathematica版本的一个缺点。和想要更改默认值评估顺序。这是可能的,但complicated,我喜欢认为未来的符号编程实现将有更优雅的方式来控制评估序列
答案 2 :(得分:9)
正如其他人已经提到的那样,Mathematica会进行大量的术语重写。也许Haskell不是最好的比较,但是Pure是一个很好的功能性术语重写语言(对于具有Haskell背景的人来说应该感觉熟悉)。也许在翻译期间阅读他们的Wiki页面会为你清理一些事情:
答案 3 :(得分:6)
Mathematica正在大量使用术语重写。该语言为各种形式的重写提供了特殊的语法,特别支持规则和策略。范式不是那么“新”,当然它并不是唯一的,但它们肯定处于这种“象征性编程”的前沿,与其他强大的玩家如Axiom一起。
至于与Haskell的比较,好吧,你可以在那里重写一下,从你的样板库中删除一些帮助,但它并不像在动态类型的Mathematica中那么容易。
答案 4 :(得分:0)
符号不应该与功能对比,而应该与数值编程相对。以MatLab与Mathematica为例。假设我想要一个矩阵的特征多项式。如果我想在Mathematica中做到这一点,可以将单位矩阵(I)和矩阵(A)本身放入Mathematica,然后执行以下操作:
Det[A-lambda*I]
然后我会得到特征多项式(没关系,可能有一个特征多项式函数),另一方面,如果我在MatLab中,我将无法使用基础MatLab,因为基础MatLab(没关系,特征多项式函数)仅擅长计算有限精度数,而不是其中存在随机lambda(我们的符号)的事物。您要做的就是购买附加的Symbolab,然后将lambda定义为其自己的代码行,然后将其写出(其中它将A矩阵转换为有理数矩阵,而不是有限精度的小数) ,尽管在这样的小情况下性能差异可能不会引起注意,但相对速度而言,它可能会比Mathematica慢得多。
这就是区别,符号语言对以完美的精度进行计算很感兴趣(通常使用有理数而不是数字),而数字编程语言则非常适合您需要进行的绝大多数计算并且它们在用于数字运算的速度上往往更快(MatLab在这方面对于高级语言几乎是无与伦比的-不包括C ++等),而在符号运算上却很糟糕。