MMA 内置的函数帮助文档

MMA 安装好后自带的说明文档在 path/to/mma/version/Documentation/ChineseSimplified 中, Packages 是内置的包文件夹, 在 MMA 中按 F1 出现的帮助文档大多来自 System 文件夹中. System 文件夹中有数据样本, 笔记样本页, 用户指南, 一些问题的处理方法专题笔记, 参考页面和教程类笔记页.

=:= 的区别

这段主要讲函数定义时, =:= 的区别. 先看两段代码

1
2
3
4
5
6
ClearAll[f];
x = 3;
f[x_] := 5 x;
f[5]
?f
(*25*)

然而

1
2
3
4
5
6
ClearAll[f];
x = 3;
f[x_] = 5 x;
f[5]
?f
(*15*)

在 MMA 中, := 用于延时计算, 也就是当左边表达式出现时, 先用右边表达式替换, 然后重新计算表达式的值. 而 = 则在函数定义时就将右边表达式的值算好赋值给左边, 函数定义中的参数 x_ 其实并没有起任何作用, 表示函数的定义域是任意表达式.

在上面用 = 定义的函数的自变量是一种模式, _ 是模式匹配符号. 比如将上面的定义换成 f[x_String]=5x, 则只有 f 作用在字符串上时, 函数的值才会是 15, 否则就不计算其值. 而事实上, 将 x_String 中的 x 删去也无妨, 因为它并不起任何作用.

1
2
3
4
5
6
ClearAll[f];
x = 3;
f[x_String] = 5 x;
f[5]
f["5"]
?f

有时我们需要进行函数迭代运算, 迭代过程中的某些函数值可能会经常被计算, 比如

1
2
3
4
5
ClearAll[FibonacciF];
FibonacciF[1] = FibonacciF[2] = 1;
FibonacciF[n_ /; n > 2] := FibonacciF[n - 1] + FibonacciF[n - 2];
?FibonacciF
TracePrint[FibonacciF[5]]

其中 TracePrint 打印出 MMA 在计算 FibonacciF[5] 时的计算过程. 如果要计算前几个 Fibonacci 数列的值, 位于前面的 FibonacciF[n] 会被重复计算多次. 为了避免重复计算造成的资源浪费, 可以通过如下代码, 使用 = 将已经计算好的值存在函数 FibonacciF[n] 中.

1
2
3
4
5
6
7
ClearAll[FibonacciF];
FibonacciF[1] = FibonacciF[2] = 1;
FibonacciF[n_ /; n > 2] :=
FibonacciF[n] = FibonacciF[n - 1] + FibonacciF[n - 2];
?FibonacciF
FibonacciF[9]
TracePrint[FibonacciF[6]]

上述代码中在计算 FibonacciF[9] 时通过递归定义逐层计算, 也就是在 FibonacciF[9] 的计算中同时计算了前 8 个 Fibonacci 数. 然而, 由于在定义 FibonacciF 时延迟定义一个赋值语句 FibonacciF[n] = FibonacciF[n - 1] + FibonacciF[n - 2], 它把第 $n$ 个值的计算结果延迟赋值给 FibonacciF[n], 这在再次遇到需要计算 FibonacciF[n] 时避免了再次递归计算, 也正因此节约了计算资源.

Pattern 模式

s:obj 表示赋予名称 s 的模式对象为 obj. 其中 s 必须是一个符号.

  • 对象 obj 可以是任何模式对象;
  • 当使用一个变换规则时(如: s_Integer :> s^2), 在右边出现的 s 由在左边匹配的任何表达式替换;
  • 运算符 : 有一个相对低的优先级. 表达式 x:_+_ 解释为 x:(_+_), 而不是 (x:_)+_.
  • 形式 s_ 等价于 s:_. 类似的, s_h 等价于 s:_h, s__ 等价于 s:__ 等等.

MMA 的 $Path 环境变量

在 MMA 中, 可以自定义包, 然后通过 Get 函数(或者使用简记符号 << 加载自定义的包), 使用自定义包的好处是可以一次性加载平时常用的函数功能. 比如在 MMA 中依次点击 新建->程序包/脚本->Wolfram 语言程序包 (.wl), 编辑以下功能并保存到指定目录:

1
2
3
4
5
6
7
BeginPackage["Functions`"]
ScreenShot::usage = "ScreenShot[img]: 将截图去除空白边界, 再在上下左右加入等间距空隙, 加上红色边框."
Begin["Private`"]
ScreenShot[img_] :=
ImagePad[ImagePad[ImageCrop[img], 50, White], 4, Red]
End[]
EndPackage[]

然后再新建 Functions.nb 文件(这里为了统一方便起见, 文件名 Functions.nb 与包名 Functions` 保持一致), 通过 <<"上述文件的绝对地址" 来调用程序包, 这个命令在执行时便自动编译了上面的 ScreenShot[img] 函数, 在 MMA 中就可以直接通过 Functions`ScreenShot[img] 来调用.

但是, 对于安装好的没有进行过任何配置的 MMA, 通过 Get 获取自定义的程序包要指定包的精确路径. 而如果程序包编辑的越来越多, 文件包所在的目录编辑起来会显得冗余, 如果想要像 MMA Documentation 中的示例一样直接 Get 自定义程序包, 那会非常方便. 只需要在 $Path 环境变量中加入程序包的目录就好了, 这个环境变量可以加在 MMA 每次启动 AutoLoad 文件(它位于 mma-install-path/AddOns/Autoload/ChineseSimplified/Kernel/init.m), 打开并编辑它, 比如(第一行是系统本来就有的, 第二行相应的修改成自己的文件目录即可):

1
2
AppendTo[$Path,ToFileName[{$InstallationDirectory,"Documentation","ChineseSimplified","System"}]];
AppendTo[$Path,ToFileName[{"root-path","directory-name-1","directory-name-2",...}]];

如此, 在每次启动 MMA 时, 系统环境变量 $Path 中都会有上述目录, 就可以直接通过 <<Functions` 来直接导入 Functions 包了.

内置变量 $UserBaseDirectory

$UserBaseDirectory: 给出基础目录,Wolfram 系统装入的用户特定文件采用常规方式存放其中. 这个子目录在用户第一次运行 MMA 时自动生成. 环境变量 $Path 自动包含这个子目录.

上面的方法可能还是太麻烦了, 对于普通用户要改动系统文件还是略有风险的, 其实在 MMA 软件中另有一个可以安装自定义包的渠道, 如下图.

选择安装包

安装的包会在 $Path 环境变量的某一目录中, 这个目录就是 FileNameJoin[{$UserBaseDirectory, "Applications"}], 它在我的 MMA Notebook 中的运行结果是 C:\Users\math\AppData\Roaming\Mathematica\Applications, 打开这个目录就会看到刚刚安装的 Functions.wl. 如果再次安装, 则会覆盖这个文件来更新自定义的包.

由于 $UserBaseDirectory 子目录在 $Path 中位于 $BaseDirectory 子目录前. 尽量不要安装系统重名的包, 以防影响系统包的调用. 使用菜单 安装 用户自定义的包的好处是当更新 MMA 时, 不用重复上面的编辑系统文件就可以像旧版 MMA 一样使用用户自定义的包.

通过 Paclets 安装包

Paclets 是 MMA 标准的安装包发行格式, Paclets 软件包是一个使用 PacletInfo.m 文件压缩的一系列源代码,文件提供了关于软件包的元信息, 类似于 C++ 中的头文件。通过 PacletInstall 安装的 Paclet 可以通过 Get 加载。在 MMA 13.0 版本中内置了 Paclets 管理器, 通过 Needs["PacletManager`"] 来导入这个管理器. 其中可用的函数有

Paclets*

函数 PacletFind 用来查找小的数据包, 用来确认准备安装的包是否存在, 内置有帮助文档. 函数 PacletInstall 用来安装数据包, PacletUninstall 卸载数据包. 安装程序包也可用使用 .paclet 扩展名的文件来安装, 比如 BoolEval 程序包可以通过 PacletInstall["/path/to/BoolEval.paclet"] 来安装. 数据包一般安装在目录 $UserBasePacletsDirectory 中, 在 Windows 下位于C:\Users\math\AppData\Roaming\Mathematica\Paclets\Repository.

其它资源

一个不低于 10.0 的 Paclet 包托管服务器: PacletServer.

MMA 的清理资源34

当 MMA 打开并运行一段时间后, 经常在后台闲置不用, 如果此时想要打开大的项目运行, 但又不想重启 MMA, 又想把之前的历史计算内存清理干净的几种办法.

  1. 直接菜单中依次点击: 计算(V)->退出内核->Local. 此命令可行, 但如果长时间运行 MMA 而囤积了大量计算结果, 用这种方式要清理很久, 而且 MMA 的 UI 看起来像是卡死了一样.
  2. 在运行新项目之前执行函数 Quit[], 可以选择性的关闭不再使用的历史 nb 文件.
  3. Clear[a]Remove[a] 清理变量名为 a 的数据, Clear["Global\`*"] 清理上下文为 Global` 的所有变量.

有时候我会自己实践一些占用计算资源比较大的项目, 也经常用 Clear[] 函数和 ClearAll[] 函数清理变量和函数的定义. 但是当变量比较多时, 逐个清理有点太麻烦了, MMA 有一个方便的清理方式如下, 只需加载笔记本的最前面即可. 它会释放内存并清理 In 和 Out 中的数据并将其编号初始化为 0, 在下次计算时 从 In[1] 和 Out[1] 开始编号.

1
2
3
Needs["Utilities`CleanSlate`"]

CleanSlate[]

一般, MMA 中定义的变量存放在一个上下文名为 Global` 的前缀中, 可以通过 ?Global`* 来查看所有已经定义过的变量和函数名. 上面的 CleanSlate[] 函数可以指明要清理的 Contexts, 但是当笔记本的上下文设置成 只限于当前笔记本 时, CleanSlate[] 并不能清理笔记本中定义的变量所存储的数据.

使用 CleanSlate[] 可能还存在其它问题, 如果在 1.nb 中定义了变量 a, 但是没有清理. 然后新建 2.nb 文件可以执行 CleanSlate[] 清理变量 a 存储的数据. 但是如果在新建 2.nb 时先执行了 Needs["Utilities`CleanSlate`"], 再次执行 CleanSlate[] 函数则不能清理变量 a 存储的数据.

References

1. MMA 自定义包, http://packagedata.net/.
2. https://paclets.github.io/PacletServer/
3. wolfram-mathematica - 通过不使用评估菜单的命令退出mathematica中的内核, https://www.coder.work/article/6902732
4. Quit Kernel in mathematica by a command not using Evaluation menu, https://stackoverflow.com/questions/6152023/quit-kernel-in-mathematica-by-a-command-not-using-evaluation-menu