Wolfram Language 学习笔记

大学四年,不应去追求成功,而应该追求自身的成长,找到自己可以持续投入的东西。
——《CS 自救指北》CXS

Wolfram Language 是一种自由的语言,你可以用你喜欢的任何方式去学习它、使用它。我本希望通过它学习函数式编程 Functional Programming,无奈相关资源太少,最后还是决定从 Haskell 入门,研读《Haskell 趣学指南》,同时尝试用 Wolfram Language 实现书中的 Haskell 代码。

事实证明,这的确有一定挑战性:已经小成的 C/C++、刚入门的 Python,不学自会的 Java、正在学的 Haskell、Wolfram Language 快把爷整精分了。不过多语言同时学还是有点好处的,可以比较同一功能不同语言的实现,从而更深入地理解这一功能;“精分” 的过程同样也帮助了对不同语言语法的记忆。

在这里点名表扬 Wolfram Language 官方的文档,至少和 Python 是一个级别的了,非常详细。可惜入坑指南和 Python 比还有一定差距,过于简陋。所以本文也希望成为通过 Wolfram Language 学 FP 一篇不错的入坑指南。因为官方文档做的非常好,所以 凡是能通过看文档学的内容一律给文档链接,不再赘述。

调用函数

Wolfram Language 不仅支持普通的前缀调用,还支持以中缀、后缀的方式调用函数。合理使用这三种调用方式,能使代码逻辑清晰,增强可读性。

前缀调用

ReferencePrefix

  1. @ 与 @@

    注意区别 @ Prefix@@ Apply@ 相当于一对中括号,而 @@ 会将其右边的函数头用左边的替换。

    1
    2
    f @ g[x] <=> f[g[x]]
    f @@ g[x] <=> f[x] (* g 被 f 替换 *)

    所以你知道下面四种写法结果不同的原因了吗?(提示:可以用 FullForm 函数观察表达式的完整形式,注意 {} 只是语法糖,本质上也是函数调用)

    1
    2
    3
    4
    Plus [1,2] (* Result: 3 *)
    Plus [{1,2}] (* Result: {1,2} *)
    Plus @ {1,2} (* Result: {1,2} *)
    Plus @@ {1,2} (* Result: 3 *)

    ReferencePostfix with two arguments

  2. @ 实现多参数调用

    @ 只能用于单参数调用,如果你实在想让 @ 支持多参数调用,有下面两种方案,分别对应参数后置、参数前置两种选择:

    1
    2
    Plus @ Sequence[1,2,3] (* Result: 6 *)
    Plus [#,2,3]& @ 1 (* Result: 6 *)

    第一种方法使用 Sequence 函数拼接参数序列,详细使用参见Sequence

    第二种方式使用纯函数语法改造原有函数。看上去这两种方法很有些费力不讨好的味道,但的确值得学习,这体现了 Wolfram Language 语法上的灵活性。

    ReferenceStack Exchange: Difference between @ and @@

中缀调用

ReferenceInfix

注意:中缀调用 ≠ 嵌套调用

1
2
{1,2,3}~Join~{4,5,6}~Join~{7,8,9} <=> Join [{1,2,3},{4,5,6},{7,8,9}]
1~Plus~2~Minus~3 != Minus[Plus[1,2]]

后缀调用

ReferencePosfix

1
{{1,2,3},{4,5,6},{7,8,9}} // Flatten // Total

同样可以使用纯函数语法改造原有函数,以实现 多参数调用

1
1 // Plus[#,2,3]& (* Result: 6 *)

Map

ReferenceMap

Apply

ReferenceApply

定义函数

Reference函数与程序

官方文档非常详细,这里不再赘述。但提醒一点:过程式语法中,函数体是用小括号 () ,而不是中括号、大括号括起来的。可以显式地使用 Return 函数以设置一个或多个出口;也可以省去最后一个表达式末尾的分号,这样会默认返回该表达式的值。但可以看到,后一种方法最多也只能设置一个出口。

模式匹配

Reference规则与模式

  1. x_ :只匹配 一个 表达式
  2. x__ : 匹配 1 个或多个 表达式
  3. x___ :匹配 0 个、1 个或多个 表达式

定义函数时使用模式匹配,可以省去一大串难看的 If 树。下面以 递归计算斐波拉契数列快速排序 为例,体会模式匹配的强大:

1
2
3
4
5
6
(*Fibonacci Sequence*)
Fib[1]:=1
Fib[2]:=1
Fib[x_]:=Fib[x-1]+Fib[x-2]
------
Fib[10] (* Result: 55 *)
1
2
3
4
5
6
7
8
9
(*Quick Sort*)
qsort[{}]:={}
qsort[l_]:=Join[
qsort[Select[Rest[l],(#<=First[l])&]],
{First[l]},
qsort[Select[Rest[l],(#>First[l])&]]
]
------
{1,1,4,5,1,4} // qsort (* Result: {1,1,1,4,4,5} *)

纯函数

纯函数递归

Wolfram Language 中,#0 就是纯函数自己,可以用来实现纯函数递归。要在 Python 中实现这一点可不容易。

1
2
If[#==1,1,#*#0[#-1]]&[3] (*计算 3 的阶乘*)
If[#==0||#==1,1,#0[#-1]+#0[#-2]]&[5] (*计算斐波拉契数列第 5 项*)

函数组合

ReferenceComposition

局部化变量

ReferenceModule

Wolfram Language 中似乎没有一般编程语言中 作用域 的概念,所有变量如果不加约束,都是全局的。很多时候这会带来不便,比如下面版本的快速排序就因此无法得到正确的答案:

1
2
3
4
5
6
7
8
qsort[{}]:={}
qsort[l_]:=(
fst=First[l];
rst=Rest[l];
qsort[Select[rst,(#<=fst)&]]~Join~{fst}~Join~qsort[Select[rst,(#>fst)&]]
)
------
qsort[{1,1,4,5,1,4}] (* Result: {1,1,1} *)
1
2
3
4
5
6
qsort[{}]:={}
qsort[l_]:=Module[{fst=First[l],rst = Rest[l]},
Join[qsort[Select[rst,(#<=fst)&]],{fst},qsort[Select[rst,(#>fst)&]]]
]
------
qsort[{1,1,4,5,1,4}] (* Result: {1,1,1,4,4,5} *)

扔掉循环

如果你学过任何一门面向过程的编程语言,如 C++,那你一定学过循环结构。虽然 Wolfram Language 支持面向过程编程范式,C/C++ 的语法略作修改就能跑起来,但速度往往很不理想。

画图

Work with $\LaTeX$

MaTex 可以让我们协同使用 $\LaTeX$ 和 Wolfram Language 这两大神器,下面给出 Mac OS 的安装方法,其他平台安装方式见 GitHub 地址.

  1. 安装 MacTex
  2. 打开 Mathematica ,执行 ResourceFunction["MaTeXInstall"][] 即可安装 MaTex
  3. 执行 <<MaTeX` 加载 MaTex(别丢了最后的反引号)
  4. 输入 MaTeX["x^2"] 看看是不是已经能够使用了

XeLaTex 中文支持

MaTex 默认使用 PDFLaTex 渲染,但 Mac OS 上使用 XeLatex 会比 PDFLaTex 更方便:一来可以直接调用系统字体,二来原生 Unicode 支持不要太香。

一行命令将 PDFLaTex 换为 XeLatex:

1
ConfigureMaTeX["pdfLaTeX" -> "/Library/TeX/texbin/xelatex"]

下面添加常用中文宏包:

1
2
3
4
5
6
7
SetOptions[
MaTeX, "BasePreamble" -> {
"\\usepackage{lmodern,exscale}",
"\\usepackage{amsmath,amssymb}",
"\\usepackage{xeCJK}"
}
]

试试执行 MaTeX["Miao \\text{猫猫能有什么坏心眼呢}"],看中文是否已经可以显示。

自动加载

每次使用都要执行 <<MaTeX` 实在是有些麻烦,可以将其加入 init.m 中,实现打开时自动加载。修改 init.m 的具体方法参见 Wolfram 系统配置文件init.m

Mac OS 下做如下修改即可:

./Library/Mathematica/Kernel/init.m
1
2
3
4
5
6
7
8
9
10
(** User Mathematica initialization file **)
<<MaTeX`
ConfigureMaTeX["pdfLaTeX" -> "/Library/TeX/texbin/xelatex"]
SetOptions[
MaTeX, "BasePreamble" -> {
"\\usepackage{lmodern,exscale}",
"\\usepackage{amsmath,amssymb}",
"\\usepackage{xeCJK}"
}
]
p1

更多教程移步 开发者博客,在 Gitter 讨论组 获得帮助。