【问题标题】:When to use Hold / ReleaseHold in Mathematica?何时在 Mathematica 中使用 Hold / ReleaseHold?
【发布时间】:2011-07-15 11:09:40
【问题描述】:

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

以下代码表示创建场景图形对象(从 XML 文件)的静态工厂方法。 (输出)字段是 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
 ];

我的问题是:

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]];

在哪里

  addAschild 

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

  addAsChild[parent MathObject, child MathObject], or
  addAsChild[parent MathObject, Children List{MathObject, ...}]

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

  <sphere point='{0., 1., 3.}'
  radius='1'
  hue='0.55' />

如果我不使用 Hold[] , ReleaseHold[] 我最终会得到类似的 objectdata

  {"GE", {"SP", {CScenegraph`point, CScenegraph`rad}}, {CScenegraph`hue}}

虽然我已经预料到了

  {"GE", {"SP", {{4., 3., -4.}, 3.}}, {0.45}}

(上面带有 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]];

到:

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

【问题讨论】:

    标签: wolfram-mathematica


    【解决方案1】:

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

    由于您的代码示例不是独立的,因此很难确定发生了什么。要确定的一件事是,OO-System 使用上下文执行非平凡的操作,因为它使用上下文作为封装机制(这是有道理的)。通常,RuleRuleDelayed 会在 r.h.s. 中注入匹配的表达式,因此尚不清楚这是如何发生的。这是一种可能的情况(您可以在笔记本中执行此操作):

    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`"]
    

    现在,

    In[71]:= Test`fn[{XMLElement[{"a"->1,"b"->2},{},{}],{"a"->3,"b"->4},{"a"->5,"b"->6}}]
    Out[71]= {{Test`c,Test`d}}
    

    发生的情况是,由于我们在 XMLElement[...]-&gt;rhs 中使用了 Rule,所以 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}]}
    

    这里的结果不同,因为fn1的实现中使用了成语XMLElement[...] :&gt; rhs,这次涉及RuleDelayed。因此,f[{a,b}] 直到 ab 被 l.h.s. 中的匹配数字替换后才被评估。并且由于f对于2个数字列表形式的参数没有规则,所以返回。

    您使用Hold-ReleaseHold 的方法起作用的原因是,这阻止了 r.h.s. (在我的示例中为函数f,在您的原始示例中调用new)从评估直到模式变量的值被替换到其中。作为旁注,您可能会发现向您的构造函数添加更好的错误检查很有用(如果 OO-System 允许),以便在运行时更好地诊断此类问题。

    所以,底线:使用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}
    

    人们可能会想出更明智的例子。这对于诸如代码生成之类的事情特别有用——可以找到一个不那么琐碎的例子here。正如我已经提到的,手头的具体案例并不真正属于 Hold-ReleaseHold 有益的案例类别 - 它们在这里只是一种解决方法,当您使用延迟规则时,这并不是真正必要的。

    【讨论】:

    • Leonid (这是仍然需要大量工作的胚胎代码,但我必须从一些东西开始。) RuleDelayed 对我来说相当新,我会阅读这个,毫无疑问你是对的不过关于它。添加 ToExpression 是尝试在我的试错会话中修复它。我会尝试在没有它的情况下运行。 - 再次感谢您的帮助。
    • @ndroock1 我发现自己使用RuleDelayedRule 更频繁,这正是我上面描述的原因。一般来说,它更安全。我在这里介绍了这些主题:mathprogramming-intro.org/book/node149.html,虽然是在相当初级的水平上。通过阅读过去的 SO Mathematica 问题,您可以找到很多涉及它的示例:RuleDelayed 在 mma 编程中确实无处不在。阅读它是一个非常好的主意,因为学习它的用法对于创建重要的 mma 程序至关重要。
    • Leonid,一定会读到这个话题。 - 我试过了,但在这个阶段不能错过 ToExpression(在 Sphere 构造函数中,我添加了平移向量,因此它必须包含数字数据。)无论如何,以后可能会在代码清理中。
    • @ndroock1 你是对的ToExpression - 我的错。它们只是在错误的方法中无用(没有Hold-ReleaseHold),其中符号没有被值替换。由于值是字符串,它们应该被解析为数字。我将编辑我的答案。
    • @_Leonid 我已经在问题下方的第 1 部分总结了您的答案。
    猜你喜欢
    • 1970-01-01
    • 2010-12-09
    • 1970-01-01
    • 2023-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-27
    • 2012-01-11
    相关资源
    最近更新 更多