【问题标题】:Mathematica: what is symbolic programming?Mathematica:什么是符号编程?
【发布时间】:2011-05-24 18:41:38
【问题描述】:

我是 Stephen Wolfram 的忠实粉丝,但他绝对不会羞于自吹自擂。在许多参考资料中,他都称赞 Mathematica 是一种不同的符号编程范式。我不是 Mathematica 用户。

我的问题是:这个符号编程是什么?它与函数式语言(如 Haskell)相比如何?

【问题讨论】:

    标签: programming-languages haskell functional-programming wolfram-mathematica


    【解决方案1】:

    当我听到“符号编程”这个短语时,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 编程都属于这种性质。

    函数式 vs. 符号式

    我不会详细讨论符号编程和函数式编程之间的区别,但我会提供一些评论。

    人们可以将符号编程视为对以下问题的回答:“如果我尝试仅使用表达式转换对所有事物进行建模会发生什么?”相比之下,函数式编程可以被视为以下问题的答案:“如果我尝试仅使用函数对所有事物进行建模会发生什么?”就像符号编程一样,函数式编程可以很容易地快速构建抽象层。我在这里给出的示例可以很容易地在 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]
    

    向世界资源研究所正式报告并得到其承认。回复:避免使用Function[var, body]Function[body]可以)。

    【讨论】:

    • 世界资源研究所真的建议你避免使用Function[var, body]吗?这很奇怪,因为它在文档中被推荐......
    • @Simon:是的,我有一封来自 WRI 的电子邮件,其中指出如果我担心函数的语义会根据“调用链”中的任何调用者是否碰巧使用类似而改变-命名符号,那么我应该避免使用Function[var, body]。没有解释为什么无法解决这个问题,但我推测由于Function 自 1.0 以来一直存在,因此在游戏后期改变其行为将是灾难性的。这个问题在(稍微)更详细的here中描述。
    • 鉴于其内部在 mma 中的暴露程度,我什至不确定 Function 是否可以治愈,即使在原则上 - 至少在 Rule 和 @987654341 的当前侵入语义下@,不尊重内部范围构造的绑定,包括它们自己。在我看来,这种现象与RuleRuleDelayed 的属性有关,而不是与Function 相关。但无论哪种方式,我同意现在改变这一点是非常危险的。太糟糕了,因为不应使用Function[var,body] - 在大型项目中几乎不可能发现此类错误。
    • @WReach Mathics,这是一个 FOSS Mathematica 克隆,没有泄漏函数问题!我刚试过。
    • @M.Aaggan FYI,看起来 Mathics 已经移动 here,它没有超过第一个版本 (1.0),但它有一些相当近期的提交(2018 年 11 月)跨度>
    【解决方案2】:

    您可以将 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[some stuff] 替换为用户单击的按钮的值”。

    可以使用条件定义规则,这些条件本身需要经过规则重写才能生成TrueFalse。例如,假设您发明了一种新的方程求解方法,但您认为它仅在您的方法的最终结果为正时才有效。您可以执行以下规则

     solve[x + 5 == b_] := (result = b - 5; result /; result > 0)
    

    这里,solve[x+5==20] 被替换为 15,但 solve[x + 5 == -20] 没有改变,因为没有适用的规则。阻止此规则应用的条件是/;result>0。 Evaluator 本质上是查看规则应用的潜在输出来决定是否继续它。

    Mathematica 的求值器贪婪地用适用于该符号的规则之一重写每个模式。有时您希望获得更好的控制,在这种情况下您可以定义自己的规则并像这样手动应用它们

    myrules={area->Pi radius^2,radius->5}
    area//.myrules
    

    这将应用myrules 中定义的规则,直到结果停止更改。这与默认评估器非常相似,但现在您可以有几组规则并有选择地应用它们。更高级的example 展示了如何制作一个类似 Prolog 的评估器,用于搜索规则应用程序的序列。

    当您需要使用 Mathematica 的默认评估器(使用 IntegrateSolve 等)并且想要更改默认的评估顺序时,会出现当前 Mathematica 版本的一个缺点。这是可能的,但complicated,我喜欢认为符号编程的一些未来实现将有一种更优雅的方式来控制评估序列

    【讨论】:

    • @Yaro 我猜当你得到规则作为函数的结果时事情会变得更有趣(如 Solve、DSolve 等)
    • 但你可以认为Solve只是另一套重写规则。当您给出一些 Mathematica 无法求解的方程时,Solve[hard_equations] 仍为 Solve[hard_equations],您可以定义适用于这种情况的自定义 Solve 规则。在这种情况下,我猜他们使用 /;有条件地为“任何可以用 Mathematica 中的方法求解的方程”定义模式,因此对于硬方程,内置规则不适用,Solve 保持原始形式
    • 我认为这太复杂了,Mathematica 程序基本上只是一组替换规则。执行是将现有规则应用于输入直到没有规则匹配的过程
    • +1 非常好的不费吹灰之力的解释。也许我唯一想补充的是,内核中已经包含了数以万计的规则和算法,它们代表了大多数语言中几乎所有可用的数学库,然后还有更多。
    • Simon,lambda 演算本身只是重写系统之一。术语重写是比任何特定 TRS 更通用的方法。
    【解决方案3】:

    正如这里其他人已经提到的,Mathematica 做了很多术语重写。尽管 Haskell 并不是最好的比较,但Pure 是一种很好的函数式术语重写语言(具有 Haskell 背景的人应该会觉得很熟悉)。也许阅读他们关于术语重写的 Wiki 页面会为您清除一些事情:

    http://code.google.com/p/pure-lang/wiki/Rewriting

    【讨论】:

    • 在语义上,Pure 更像是 Scheme(动态类型,默认为 Eager 评估)或 OCaml(有明确的参考单元)。但是语法和库确实反映了 Haskell 的。
    【解决方案4】:

    Mathematica 大量使用术语重写。该语言为各种形式的重写提供了特殊的语法,对规则和策略的特殊支持。这种范式并不是那么“新”,当然也不是独一无二的,但它们绝对处于这种“符号编程”事物的最前沿,与 Axiom 等其他强大的参与者并驾齐驱。

    至于与 Haskell 的比较,好吧,您可以在那里重写,并从废弃样板库中获得一些帮助,但这并不像在动态类型的 Mathematica 中那么容易。

    【讨论】:

      【解决方案5】:

      符号不应与函数式对比,而应与数值编程对比。以 MatLab 与 Mathematica 为例。假设我想要矩阵的特征多项式。如果我想在 Mathematica 中这样做,我可以将单位矩阵 (I) 和矩阵 (A) 本身放入 Mathematica,然后执行以下操作:

      Det[A-lambda*I]
      

      我会得到特征多项式(没关系可能有一个特征多项式函数),另一方面,如果我在 MatLab 中,我不能用基本 MatLab 来做,因为基本 MatLab(没关系可能有一个特征多项式函数)只擅长计算有限精度数,而不是其中有随机 lambda(我们的符号)的东西。您需要做的是购买附加的 Symbolab,然后将 lambda 定义为它自己的代码行,然后将其写出来(其中它会将您的 A 矩阵转换为有理数矩阵,而不是有限精度小数) ,虽然对于这样的小情况,性能差异可能不明显,但就相对速度而言,它可能会比 Mathematica 慢得多。

      这就是区别,符号语言有兴趣以完美的精度进行计算(通常使用有理数而不是数值),而数值编程语言则非常擅长您需要进行的绝大多数计算而且它们在数字运算方面往往更快(在这方面,MatLab 在高级语言方面几乎是无与伦比的 - 不包括 C++ 等),并且在符号运算方面表现不佳。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-02-13
        • 1970-01-01
        • 2016-04-21
        • 1970-01-01
        • 1970-01-01
        • 2010-11-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多