Accidental shadowing and `Removed[symbol]`

2019-02-19 03:15发布

If you evaluate the following code twice, results will be different. Can anyone explain what's going on?

findHull[points_] := Module[{},
   Needs["ComputationalGeometry`"];
   ConvexHull[points]
   ];
findHull[RandomReal[1, {10, 2}]];
Remove["Global`ConvexHull"];
findHull[RandomReal[1, {10, 2}]]

2条回答
放我归山
2楼-- · 2019-02-19 03:38

Not a direct answer to the question, but a bit too large for a comment. As another alternative, a general way to delay the symbol parsing until run-time is to use Symbol["your-symbol-name"]. In your case, you can replace ConvexHull on the r.h.s. of your definition by Symbol["ConvexHull"]:

findHull[points_] := 
 Module[{}, 
    Needs["ComputationalGeometry`"];
    Symbol["ConvexHull"][points]];

This solution is not very elegant though, since Symbol["ConvexHull"] will be executed every time afresh. This can also be somewhat error-prone, if you do non-trivial manipulations with $ContextPath. Here is a modified version, combined with a generally useful trick with self-redefinition, that I use in similar cases:

Clear[findHull];
findHull[points_] :=
Module[{},
  Needs["ComputationalGeometry`"];
  With[{ch = Symbol["ConvexHull"]},
    findHull[pts_] := ch[pts];
    findHull[points]]];

For example,

findHull[RandomReal[1, {10, 2}]]

{4, 10, 9, 1, 6, 2, 5}

What happens is that the first time the function is called, the original definition with Module gets replaced by the inner one, and that happens already after the needed package is loaded and its context placed on the $ContextPath. Here we exploit the fact that Mathematica replaces an old definition with a new one if it can determine that the patterns are the same - as it can in such cases.

Other instances when self-redefinition trick is useful are cases when, for example, a function call results in some expensive computation, which we want to cache, but we are not sure whether or not the function will be called at all. Then, such construct allows to cache the computed (say, symbolically) result automatically upon the first call to the function.

查看更多
Evening l夕情丶
3楼-- · 2019-02-19 03:44

The problem is that even though the module is not evaluated untill you call findHull, the symbols are resolved when you define findHull (i.e.: The new downvalue for findHull is stored in terms of symbols, not text). This means that during the first round, ConvexHull resolves to Global`ConvexHull because the Needs is not evaluated. During the second round, ComputationalGeometry is on $ContextPath and so ConvexHull resolves as you intended.

If you really cannot bear to load ComputationalGeometry beforehand, just refer to ConvexHull by its full name: ComputationalGeometry`ConvexHull. See also this related answer.

HTH

查看更多
登录 后发表回答