【问题标题】:Specifics of usage and internal work of *Set* functions*Set* 函数的使用细节和内部工作
【发布时间】:2011-05-01 05:52:51
【问题描述】:

我刚刚注意到 Mathematica*Set* 函数内部工作的一个未记录的特性。

考虑:

In[1]:= a := (Print["!"]; a =.; 5);
a[b] = 2;
DownValues[a]

During evaluation of In[1]:= !

Out[3]= {HoldPattern[a[b]] :> 2}

但是

In[4]:= a := (Print["!"]; a =.; 5);
a[1] = 2;
DownValues[a]

During evaluation of In[4]:= !

During evaluation of In[4]:= Set::write: Tag Integer in 5[1] is Protected. >>

Out[6]= {HoldPattern[a[b]] :> 2}

造成这种差异的原因是什么?为什么a 被评估,尽管Set 具有属性HoldFirst?这种行为对哪些目的有用?

还要注意这种情况:

In[7]:= a := (Print["!"]; a =.; 5)
a[b] ^= 2
UpValues[b]
a[b]

During evaluation of In[7]:= !

Out[8]= 2

Out[9]= {HoldPattern[5[b]] :> 2}

Out[10]= 2

如您所见,我们得到了5[b] 的工作定义,避免了标签IntegerProtected 属性,这在通常情况下会导致错误:

In[13]:= 5[b] = 1

During evaluation of In[13]:= Set::write: Tag Integer in 5[b] is Protected. >>

Out[13]= 1

避免此错误的另一种方法是使用TagSet*

In[15]:= b /: 5[b] = 1
UpValues[b]

Out[15]= 1

Out[16]= {HoldPattern[5[b]] :> 1}

为什么会有这些功能?


关于我的问题,为什么我们可以写a := (a =.; 5); a[b] = 2 而不能写a := (a =.; 5); a[1] = 2。在 Mathematica 5 中,我们也不能写 a := (a =.; 5); a[b] = 2

In[1]:=
a:=(a=.;5);a[b]=2
From In[1]:= Set::write: Tag Integer in 5[b] is Protected. More...
Out[1]=
2

(以上是从Mathematica 5.2复制而来)

当我们评估 a := (a =.; 5); a[b] = 2 时,我们可以看到 Mathematica 的新版本内部发生了什么:

In[1]:= a:=(a=.;5);
Trace[a[b]=2,TraceOriginal->True]
Out[2]= {a[b]=2,{Set},{2},a[b]=2,{With[{JLink`Private`obj$=a},RuleCondition[$ConditionHold[$ConditionHold[JLink`CallJava`Private`setField[JLink`Private`obj$[b],2]]],Head[JLink`Private`obj$]===Symbol&&StringMatchQ[Context[JLink`Private`obj$],JLink`Objects`*]]],{With},With[{JLink`Private`obj$=a},RuleCondition[$ConditionHold[$ConditionHold[JLink`CallJava`Private`setField[JLink`Private`obj$[b],2]]],Head[JLink`Private`obj$]===Symbol&&StringMatchQ[Context[JLink`Private`obj$],JLink`Objects`*]]],{a,a=.;5,{CompoundExpression},a=.;5,{a=.,{Unset},a=.,Null},{5},5},RuleCondition[$ConditionHold[$ConditionHold[JLink`CallJava`Private`setField[5[b],2]]],Head[5]===Symbol&&StringMatchQ[Context[5],JLink`Objects`*]],{RuleCondition},{Head[5]===Symbol&&StringMatchQ[Context[5],JLink`Objects`*],{And},Head[5]===Symbol&&StringMatchQ[Context[5],JLink`Objects`*],{Head[5]===Symbol,{SameQ},{Head[5],{Head},{5},Head[5],Integer},{Symbol},Integer===Symbol,False},False},RuleCondition[$ConditionHold[$ConditionHold[JLink`CallJava`Private`setField[5[b],2]]],False],Fail},a[b]=2,{a[b],{a},{b},a[b]},2}

看到对 Java 的调用以纯粹的与语言相关的操作(例如为变量赋值)时,我感到非常惊讶。使用 Java 进行此类操作是否合理?


Todd Gayley(Wolfram Research)has explained 这种行为:

首先,让我指出,在 Mathematica 8, J/Link 不再 重载集。内部内核 建立机制,其中 其他事情,允许 J/Link 避免 需要特殊的,呃,“技巧” 与集合。

J/Link 从 一开始,将近十二年 前。这允许它支持这个 为 Java 赋值的语法 字段:

 javaObject@field = value

Set 的重载定义 导致任务的放缓 表格

 _Symbol[_Symbol] = value

当然,分配很快 操作,所以减速很小 实际条款。只有高度专业化 程序类型可能是 受到很大影响。

Set 重载不会导致 调用 Java 的任务 不涉及 Java 对象(这将 非常昂贵)。这可以验证 通过简单地使用 TracePrint on 你的 a[b]=c。

正如您所注意到的,它确实使 作业行为的改变 匹配 _Symbol[_Symbol] = 值。 具体来说,在 f[_Symbol] = value, f 被评估两次。这可能导致 以下代码的问题 (非常不寻常的)形式:

 f := SomeProgramWithSideEffects[]
 f[x] = 42

我不记得曾经见过“真实” 像这样的代码,或者看到问题 由用户举报。

在 8.0 中这一切都没有实际意义。

【问题讨论】:

  • 在我走之前,在阅读了你的最新更新之后,我只能说:那是什么鬼?大声笑
  • 我无法在 v8 中重现此行为。两个评估都会生成消息
  • @Sasha 我使用 v.7.01。顺便说一句,你通过评估DownValues[Set] 得到什么?我得到{HoldPattern[ JLink`Private`sym_Symbol[JLink`Private`arg_Symbol] = JLink`Private`val_] :> With[{JLink`Private`obj = JLink`Private`sym}, JLink`CallJava`Private`setField[ JLink`Private`obj[JLink`Private`arg], JLink`Private`val] /; Head[JLink`Private`obj] === Symbol && StringMatchQ[Context[JLink`Private`obj], "JLink`Objects`*"]]}
  • DownValues[Set] 在 Mma 8.0.1 中返回一个空列表。您显示的定义实现了对象字段分配的 JLink 语法,例如obj@field = value。在 Mma 8 中,要么 JLink 现在以另一种方式执行此操作,要么规则已移到读取保护的幕后。

标签: wolfram-mathematica


【解决方案1】:

首先以UpSet 为例,这是预期的行为。可以写:

 5[b] ^= 1

分配给b,而不是整数5

关于SetSetDelayed,虽然它们具有Hold 属性,但它们仍然在内部计算表达式。这允许诸如:

p = n : (_List | _Integer | All);

f[p] := g[n]

测试:

f[25]
f[{0.1, 0.2, 0.3}]
f[All]
g[25]
 g[{0.1, 0.2, 0.3}]
g[全部]

可以看到还评估了头部区域。这至少对UpSet 有用:

p2 = head : (ff | gg);
p2[x] ^:= Print["Echo ", head];

ff[x]
gg[x]

回声 ff

回声gg

很容易看出Set 也会发生这种情况,但我不太清楚这有什么用处:

j = k;
j[5] = 3;
DownValues[k]

(* Out=  {HoldPattern[k[5]] :> 3}  *)

我对你问题第一部分的分析是错误的。我目前不明白为什么a[b] = 2 被接受而a[1] = 2 不被接受。也许在分配的某个阶段,第二个显示为5[1] = 2,并且模式检查会引发错误,因为 LHS 上没有符号。

【讨论】:

  • 在您的示例中,仅评估函数的参数,而问题是关于函数符号的评估(l.h.s 的Head)。
  • @Alexey,请参阅帖子中间的编辑。另外,我刚才纠正了一个错误,所以请务必刷新页面。
  • 但是为什么一个人不能写a := (a =.; 5); a[1] = 2而一个人可以写a := (a =.; 5); a[b] = 2呢?
  • @Alexey,我想了几分钟我明白了,但我错了。我不知道。我唯一的想法是在我上次编辑的最后一句话中。
  • @Alexey 你可能想看看我的 cmets 到这个帖子stackoverflow.com/questions/4661583/…,它们是对@Mr.Wizard 答案的补充。
【解决方案2】:

您显示的行为似乎是在 Mathematica 8 中修复的 7.0.1(可能更早版本)中的错误。在 Mathematica 8 中,您原来的 a[b] = 2a[1] = 2 示例都给出了 Set::write ... is protected 错误.

问题似乎源于您确定的与 JLink 相关的 Set 的向下值。该规则实现了用于为 Java 对象的字段赋值的 JLink 语法,例如object@field = value.

Set 在 Mathematica 8 中没有这个定义。我们可以强制重新添加类似的定义,因此:

Unprotect[Set]
HoldPattern[sym_Symbol[arg_Symbol]=val_] :=
  With[{obj=sym}
  , setField[obj[arg], val] /; Head[obj] === Symbol && StringMatchQ[Context[obj],"Something`*"]
  ]

在 Mathematica 8 中安装此定义后,它现在表现出与在 Mathematica 7 中相同的不一致行为。

我认为 JLink 对象字段分配现在是通过其他方式完成的。有问题的规则看起来可能会为 a[b] = ... 表单的每次评估添加昂贵的 HeadStringMatchQ 测试。摆脱困境?

【讨论】:

  • @Mr.W 可以轻松删除该定义(使用Unset),但很难知道随着该删除还有哪些其他功能会被破坏。 Mma 在内部出于各种目的使用 JLink,因此可能会产生微妙而深远的后果(与在这种特殊情况下似乎咬伤了 WRI 的后果不同;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-01
  • 2015-11-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多