如果您评估以下代码两次,结果将会有所不同。谁能解释一下发生了什么?
findHull[points_] := Module[{},
Needs["ComputationalGeometry`"];
ConvexHull[points]
];
findHull[RandomReal[1, {10, 2}]];
Remove["Global`ConvexHull"];
findHull[RandomReal[1, {10, 2}]]
答案 0 :(得分:6)
问题在于,即使未评估模块,直到您调用findHull
,当您定义findHull
时,符号也会被解析(即:findHull
的新下值存储在符号的术语,而不是文本)。
这意味着在第一轮中,ConvexHull
会解析为Global`ConvexHull
,因为未评估Needs
。
在第二轮中,ComputationalGeometry
已在$ContextPath
上,因此ConvexHull
会按您的意图结算。
如果您真的无法忍受事先加载ComputationalGeometry
,请使用ConvexHull
的全名:ComputationalGeometry`ConvexHull
。另请参阅this related answer。
HTH
答案 1 :(得分:2)
不是问题的直接答案,但对评论来说有点太大了。作为另一种选择,将符号解析延迟到运行时的一般方法是使用Symbol["your-symbol-name"]
。在您的情况下,您可以替换r.h.s.上的ConvexHull
。你的定义是Symbol["ConvexHull"]
:
findHull[points_] :=
Module[{},
Needs["ComputationalGeometry`"];
Symbol["ConvexHull"][points]];
这个解决方案不是很优雅,因为Symbol["ConvexHull"]
每次都会重新执行。如果您使用$ContextPath
执行非平凡的操作,这也可能有些容易出错。这是一个修改后的版本,结合了一个通常有用的自我重新定义技巧,我在类似的情况下使用:
Clear[findHull];
findHull[points_] :=
Module[{},
Needs["ComputationalGeometry`"];
With[{ch = Symbol["ConvexHull"]},
findHull[pts_] := ch[pts];
findHull[points]]];
例如,
findHull[RandomReal[1, {10, 2}]]
{4, 10, 9, 1, 6, 2, 5}
第一次调用函数时,会发生Module
的原始定义被内部定义替换,并且在加载所需的包并将其上下文放在{{1 }}。在这里,我们利用Mathematica将旧定义替换为新定义的事实,如果它可以确定模式是相同的 - 就像在这种情况下一样。
自重新定义技巧有用的其他情况是,例如,函数调用导致一些昂贵的计算,我们想要缓存,但我们不确定是否会调用该函数。然后,这样的构造允许在第一次调用函数时自动缓存计算的(例如,符号)结果。