我非常喜欢Stephen Wolfram,但他肯定不会吝啬夸自己。在许多参考文献中,他称Mathematica为一种不同的符号编程范例。我不是Mathematica的用户。
我的问题是:什么是符号编程?它与函数式语言(如Haskell)相比如何?
我非常喜欢Stephen Wolfram,但他肯定不会吝啬夸自己。在许多参考文献中,他称Mathematica为一种不同的符号编程范例。我不是Mathematica的用户。
我的问题是:什么是符号编程?它与函数式语言(如Haskell)相比如何?
当我听到“符号编程”这个词组时,我立刻想到了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
Voila! The red circle is now black.
Symbolic programming is known for this kind of "symbol pushing," which involves transforming expressions. A majority of Mathematica programming falls under this category.
Symbolic programming attempts to model everything with expression transformations, while functional programming models everything with functions. Both paradigms allow for easy abstraction building. For instance, the example above could be replicated in Haskell by utilizing a functional reactive animation approach. Functional programming relies on function composition, higher-level functions, combinators, and other functions-related features.
Mathematica heavily favors symbolic programming, although functional features can still be utilized. However, these features are simply a thin layer over transformations and sometimes abstract poorly (see footnote below).
Conversely, Haskell specializes in functional programming, even though symbolic programming is achievable. The differences lie in the syntactic representation of programs and data, making the symbolic programming experience inferior.
I maintain that there is a clear difference between functional programming (e.g., Haskell) and symbolic programming (e.g., Mathematica). Studying both can provide a richer understanding than focusing solely on one paradigm.
Yes, indeed. Here's an example:
f[x_] := g[Function[a, x]];
g[fn_] := Module[{h}, h[a_] := fn[a]; h[0]];
f[999]
已向WRI报告并得到确认。回应是:避免使用 Function[var, body]
(Function[body]
没问题)。
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
。如果你想让 add
只用于数值类型的 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[some stuff]”。
可以使用条件定义规则,这些条件本身需要通过规则重写以产生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的求值器,它会搜索规则应用的序列。Integrate
,Solve
等)并且想要更改默认的求值顺序时,会出现问题。虽然这是可能的,但比较复杂,我认为未来某个符号编程的实现将有一种更优雅的控制求值顺序的方式。Solve
看作是另一组重写规则。当你给出一些 Mathematica 无法解决的方程时,Solve[hard_equations]
仍然保持为 Solve[hard_equations]
,你可以定义一个自定义的 Solve
规则来应用于这种情况。在这种情况下,我猜他们使用 /; 条件来定义“任何可以用 Mathematica 方法解决的方程”的模式,因此对于难解的方程,内置规则不适用,Solve
保持原始形式。 - Yaroslav Bulatov正如其他人在这里提到的那样,Mathematica进行了很多术语重写。也许Haskell并不是最好的比较对象,但Pure是一种不错的函数术语重写语言(对于有Haskell背景的人应该会感到熟悉)。也许阅读他们关于术语重写的维基页面会为您解决一些问题:
"Symbolic"与"Functional"不应该相互对比,而是应该与"数值编程"进行对比。以MatLab和Mathematica为例,假设我想要获得一个矩阵的特征多项式。如果我想在Mathematica中完成这个任务,我可以将单位矩阵(I)和矩阵(A)输入Mathematica,然后执行以下操作:
Det[A-lambda*I]
Function[var, body]
吗?这很奇怪,因为文档中推荐使用它... - SimonFunction[var, body]
。没有提供关于为什么无法修复这个问题的解释,但我猜测由于Function
从1.0版本就存在,所以在这个时候更改它的行为将是灾难性的。这个问题在这里稍微详细地描述了一下。 - WReachFunction
以及其他类似情况时都只使用槽符号表示法,因为使用命名参数会很危险。 - WReach