常见报错消息:3 不是一个变量
一些内置函数要求用户提供一个符号作为形式参数以及一个包含形式参数的表达式作为函数。如果试图通过 :=
来在一些具体数值上复用这些函数,就会出错。
以下代码试图定义
ClearAll["`*"]
g[x_] := D[x^2, x]
g[3]
但是报错了,消息是 General::ivar:
"3 is not a valid variable."
返回值也不正确:我们希望它返回 6
,而它实际返回了 D[9, 3]
。
g[3]
被计算为 D[3^2, 3]
,其第一子表达式 3^2
是一个常函数而不是一个平方函数的表达式,第二子表达式是数值 3
而不是 D
所要求的符号,自然会出错。
ClearAll["`*"]
g[x_] = D[x^2, x]
g[3]
不同之处仅在于把 SetDelayed
( :=
)换成了 Set
( =
)。它们(几乎)只有一个区别,就是 SetDelayed
具有 HoldAll
属性,而 Set
所具有的是一个“更弱的” HoldFirst
属性。
SetDelayed
的 HoldAll
属性保证了它在把 :=
两端的表达式存储为一个定义时,不会先计算两端的表达式。从而 x := Print[1]
这一含有 Print
的代码实际并不会执行 Print
指令。所以它进行的定义是 g[x_] :> D[x^2, x]
。此后当你计算 g[3]
时,3
会代入所有的 x
,然后才会计算 D[3^2, 3]
从而出错。
而 Set
的 HoldFirst
属性使得它只保持第一个子表达式不计算,当 =
右端的 D[x^2, x]
被计算为 2x
后,定义才进行,所以它进行的定义是 g[x_] :> 2x
。此后当你计算 g[3]
时,3
直接代入 2x
得到 6
。
对于定义导函数的需求,我们有一个特别简单的解决方案来替代 D
:
ClearAll["`*"]
f[x_] := x^2
g[x_] := f'[x]
g[3]
其中撇号 '
是函数 Derivative
的运算符。这个方法的存在是因为 Derivative
的设计要求它处理的是一个函数的头部(如 f
)而不是函数的表达式 x^2
,从而不需要用户告知表达式中的哪一个变量是函数的形式参数,可以自动地完成计算。
值得一提的是, Derivative
可以处理纯函数,也能计算数值导数(即插值函数 InterpolatingFunction
的差分)。一个函数不一定要与一个符号绑定,如果愿意抛弃定义 g[x_] :> something
的做法,就可以用纯函数来给出最简单的导函数表示方式:#^2&'
。
不过仍然存在很多内置函数要求用户告知表达式中的形式参数(符号计算函数如Collect
、数值计算函数如FindRoot
),而没有 Derivative
于 D
这样的替代。
在定义内部调整计算顺序和命名
ClearAll@"`*"
g[x_] := D[tmp^2, tmp] /. tmp -> x
g[3]
利用其它有 “Hold” 能力的函数来调整计算顺序(类似于宏)
ClearAll@"`*"
(g[x_] := #)&@D[x^2, x]
g[3]
ClearAll@"`*"
With[{tmp = D[x^2, x]}
g[x_] := tmp
]
g[3]
使用非标准计算避免 HoldAll
ClearAll@"`*"
g[x_] := Evaluate@D[x^2, x]
g[3]