何时在Mathematica中使用Hold/ReleaseHold?

nil*_*ock 5 wolfram-mathematica

示例和背景(注意Hold,ReleaseHold的用法):

以下代码表示用于创建scenegraph对象的静态工厂方法(来自XML文件).(output-)字段是CScenegraph(OO-System类)的一个实例.

 new[imp_]:= Module[{
 ret,
 type = "TG",
 record ={{0,0,0},"Root TG"}
 },
 ret = MathNew[
    "CScenegraph", 
    2,
    MathNew["CTransformationgroup",1,{type,record},0,0,0,0,Null]];
 ret@setTree[ret];
 ret@getRoot[]@setColref[ret];
 csp = loadClass["CSphere"];
 spheres = Cases[imp, XMLElement["sphere", _, __], Infinity];
 codesp = Cases[spheres, XMLElement["sphere", 
    {"point" -> point_, "radius" -> rad_, "hue" -> hue_}, {}] -> Hold[csp@new[ToExpression[point], ToExpression[rad], ToExpression[hue]]]];
 ret@addAschild[ret@getRoot[],ReleaseHold[codesp]];
 ret
 ];
Run Code Online (Sandbox Code Playgroud)

我的问题是关于以下内容:

spheres = Cases[imp, XMLElement[\sphere\, _, __], Infinity];
codesp = Cases[spheres, XMLElement[\sphere\, 
    {\point\ -> point_, \radius\ -> rad_, \"hue\" -> hue_}, {}] -> Hold[csp@new[ToExpression[point], ToExpression[rad], ToExpression[hue]]]];
ret@addAschild[ret@getRoot[],ReleaseHold[codesp]];
Run Code Online (Sandbox Code Playgroud)

哪里

  addAschild 
Run Code Online (Sandbox Code Playgroud)

将(几何)列表添加到(根)转换组并具有签名

  addAsChild[parent MathObject, child MathObject], or
  addAsChild[parent MathObject, Children List{MathObject, ...}]
Run Code Online (Sandbox Code Playgroud)

表示球体的XML元素如下所示:

  <sphere point='{0., 1., 3.}'
  radius='1'
  hue='0.55' />
Run Code Online (Sandbox Code Playgroud)

如果我不使用Hold [],ReleaseHold []我最终会使用objectdata

  {"GE", {"SP", {CScenegraph`point, CScenegraph`rad}}, {CScenegraph`hue}}
Run Code Online (Sandbox Code Playgroud)

虽然我本来期待的

  {"GE", {"SP", {{4., 3., -4.}, 3.}}, {0.45}}
Run Code Online (Sandbox Code Playgroud)

(上面的代码使用Hold [],ReleaseHold []生成正确的数据.)

问题

1.为什么在这种情况下保持必要?(事实上​​,是吗?有没有办法在没有Hold [],ReleaseHold []的情况下对此进行编码?)(我通过反复试验得到了它!不明白为什么.)

2.作为学习要点:使用Hold/ReleaseHold的原型示例/案例是什么?

编辑:

列昂尼德的答案摘要.更改此代码

  codesp = Cases[spheres, XMLElement["sphere", 
{"point" -> point_, "radius" -> rad_, "hue" -> hue_}, {}] -> Hold[csp@new[ToExpression[point], ToExpression[rad], ToExpression[hue]]]];
  ret@addAschild[ret@getRoot[],ReleaseHold[codesp]];
Run Code Online (Sandbox Code Playgroud)

至:

  codesp = Cases[spheres, XMLElement["sphere", 
{"point" -> point_, "radius" -> rad_, "hue" -> hue_}, {}] :> csp@new[ToExpression[point], ToExpression[rad], ToExpression[hue]]];
  ret@addAschild[ret@getRoot[],codesp];
Run Code Online (Sandbox Code Playgroud)

Leo*_*rin 4

第一个问题的简短答案是您可能应该使用而RuleDelayed不是 - Rule,然后您就不需要Hold- ReleaseHold

由于您的代码示例不是独立的,因此很难确定发生了什么。需要确定的一件事是,面向对象系统使用上下文执行重要的操作,因为它使用上下文作为封装机制(这是有道理的)。通常,RuleRuleDelayed在 rhs 中注入匹配的表达式,因此不清楚这是如何发生的。这是一种可能的情况(您可以在笔记本中执行此操作):

BeginPackage["Test`"]
f[{a_Symbol, b_Symbol}] := {c, d};
fn[input_] :=  Cases[input, XMLElement[{"a" -> a_, "b" -> b_}, {}, {}] -> f[{a, b}]];
fn1[input_] := Cases[input, XMLElement[{"a" -> a_, "b" -> b_}, {}, {}] :> f[{a, b}]];
EndPackage[];
$ContextPath = DeleteCases[$ContextPath, "Test`"]
Run Code Online (Sandbox Code Playgroud)

现在,

In[71]:= Test`fn[{XMLElement[{"a"->1,"b"->2},{},{}],{"a"->3,"b"->4},{"a"->5,"b"->6}}]
Out[71]= {{Test`c,Test`d}}
Run Code Online (Sandbox Code Playgroud)

发生的情况是,由于我们使用了Rulein XMLElement[...]->rhs,因此r.h.s.在替换发生之前进行评估 - 在本例中是函数f进行评估。现在,

In[78]:= Test`fn1[{XMLElement[{"a" -> 1, "b" -> 2}, {}, {}], 
      {"a" ->3, "b" -> 4}, {"a" -> 5, "b" -> 6}}]

Out[78]= {Test`f[{1, 2}]}
Run Code Online (Sandbox Code Playgroud)

这里的结果是不同的,因为这个习惯用法XMLElement[...] :> rhs被用在 的实现中fn1,涉及到RuleDelayed这次。因此,f[{a,b}]直到被 lhs 中的匹配数字替换后才对其求值,并且由于a没有 2 个数字列表形式的参数的规则,因此返回它。bf

Hold您的方法使用-起作用的原因ReleaseHold是,这阻止了 rhs (f我的示例中的函数,以及原始示例中的调用new)的评估,直到模式变量的值被替换为它。附带说明一下,您可能会发现向构造函数添加更好的错误检查很有用(如果 OO 系统允许),这样可以在运行时更好地诊断此类问题。

所以,底线:使用RuleDelayed,而不是Rule


要回答第二个问题,当您想要在允许计算之前操作所保存的代码时,组合ReleaseHold-通常很有用。Hold例如:

In[82]:= 
{a,b,c}={1,2,3};
ReleaseHold[Replace[Hold[{a,b,c}],s_Symbol:>Print[s^2],{2}]]

During evaluation of In[82]:= 1
During evaluation of In[82]:= 4
During evaluation of In[82]:= 9

Out[83]= {Null,Null,Null}
Run Code Online (Sandbox Code Playgroud)

人们也许可以想出更合理的例子。这对于代码生成之类的事情特别有用 - 可以在这里找到一个不那么简单的例子。Hold正如我已经提到的,当前的具体案例并不真正属于有益的案例类别ReleaseHold,它们在这里只是一种解决方法,当您使用延迟规则时,这并不是真正必要的。

  • @ndroock1 我发现自己使用“RuleDelayed”比“Rule”更频繁,这正是我上面描述的原因。一般来说,是比较安全的。我在这里介绍了这些主题:http://www.mathprogramming-intro.org/book/node149.html,尽管是相当初级的水平。通过阅读过去的 SO Mathematica 问题,您可以找到很多涉及它的示例:“RuleDelayed”在 mma 编程中确实无处不在。阅读它是一个非常好的主意,因为学习它的用法对于创建不平凡的 MMA 程序至关重要。 (2认同)