公告:本站正式转型为非交互式静态网站!
转型:本站将通过笔记和博客的形式继续为大家服务,关于 Mathematica 问答服务请移步至QQ群:365716997
联系:如有问题请联系QQ群管理员,或发送邮件至:lixuan.xyz@qq.com。
感谢:最后非常感谢大家多年来的支持与帮助!
参考《互联网跟帖评论服务管理规定》《中华人民共和国网络安全法》《网络信息内容生态治理规定》《互联网用户账号信息管理规定》

—— 2022-11-27

欢迎来到 Mathematica 问答社区

提问时请贴上文本代码

语法高亮:在编辑器中点击

被禁止的话题:广告破解

请阅读:《提问的智慧》

备用域名:mma.ooo

支持LaTex数学公式
行内公式标识符:\$ 或“$\backslash ($”+“$\backslash )$”,
行间公式标识符:\$\$ 或 “$\backslash [$”+“$\backslash ]$”

社区建议QQ群:365716997

分类

+3 投票
4.4k 浏览

比如HoldAll,我如果定义

SetAttributes[ff,HoldAll];
ff[x_,y_]:=x+y;

然后

a=1;
ff[a,2]

结果是3。

为什么结果不是 a+2 呢?因为已经用HoldAll规定了在函数里对所有变量保持不计算(unevaluated)嘛。HoldAll的定义就是不计算任何自变量,怎么到最后 ff 里的a又被计算了。

上面例子中换作HoldFirst也一样。

看来帮助文档里HoldAll和HoldFirst它们的定义似乎不完整,漏掉了何时自变量会被计算的介绍。那自变量是到什么时候会被计算的,规则是什么?

 

用户: wonderlands0 (451 分)
虽然你的a在param里面没算,但是你右边x+y的时候a就变成1了啊
你没完全理解赋值和延迟赋值的真正区别
@happyfish: 你意思是,当被hold的自变量再次被用到时,就会被计算?
@Amita: 还比如Plot是HoldAll属性的,像Plot[Through[{Sin,Cos}[x]],{x,0, 2\[Pi]}] 并没有前面的函数延迟赋值。但最后Through[{Sin,Cos}[x]]这部分还是被计算了。
哈哈哈哈哈照你这逻辑Plot之类的函数该画不出图啦哈哈哈
Clear[a, test];
ClearAttributes[test, HoldFirst];
a = 1;
test[x_] := Defer[x];
Print@test[a];
SetAttributes[test, HoldFirst];
Print@test[a]
你理解一下这一段
happyfish 谢谢!你这个我琢磨琢磨。
xzczd  额额,好冤。事实上那不是我的逻辑,而是MMA里给出的HoldFirst和HoldAll定义的逻辑。。。我问题的意思就是它在哪个环节的时候自己取消hold 。
“HoldFirst is an attribute that specifies that the first argument to a function is to be maintained in an unevaluated form.”
 
“HoldAll is an attribute that specifies that all arguments to a function are to be maintained in an unevaluated form. ”

两个定义完全没有限定自变量保持不计算(unevaluated)的时效。我这样说并不是咬文嚼字。它这样的定义从数学的严谨性上来说是有错误的。

Plot[HoldForm[Sin[x]], {x, 0, 6 Pi}]
如果像这样彻底地保持不被计算的状态,确实就是Plot不出图。
并不觉得他的描述有问题。你看一下power programming in mma第七章就知道了
好。哦对,感觉你那个例子很有用。

3 个回答

+5 投票
 
已采纳

过了一整晚还没人答啊。行啊,那我答。

简洁的回答

其实我觉得happyfish的第一条评论

虽然你的a在param里面没算,但是你右边x+y的时候a就变成1了啊

已经把这个问题解释清楚了,如果还不理解,那观察下列代码的执行结果,也该弄明白了:

ClearAll[f, x]
f[x_] = x^2;
f[1 + 1] // Trace
Attributes@f = HoldAll;
f[1 + 1] // Trace

东拉西扯的回答

这个问题真要甩锅的话确实可以甩到“帮助文档叙述不清”上,此处问题的焦点其实在于,Mathematica文档中的“函数”指的到底是什么?Mathematica常常被认为是“函数式语言”的原因到底是啥?没错,HoldAll自带帮助最上端简介

……指定一个函数的所有自变量保持不计算的形式

如果要使用较无歧义(但更艰涩)的句子来代替的话,应该是

……指定一个表达式头部(head)的所有参数(argument)保持不计算的形式

顺便值得一提的是,Attributes函数的“更多信息”里也对HoldAll的功能进行了介绍,它的介绍风格就与我上面这行差不多:

这也是我一直强调文档的“更多信息”也一定要读的原因:虽然它相对文档其他部分常常更抽象更难懂,但它往往包含许多文档其他部分所不含的细节,且叙述严谨。

好的,那么被设置了属性HoldAll的f的所有参数不计算意味着什么?请注意,当一个参数通过f内的DownValues被传递进别的函数里时,它就不是f的参数了,f的属性自然也就影响不了它了:

Clear[f, x]
Attributes@f = HoldAll;
f[1 + 1]
f[x_] = x;
f[1 + 1] // Trace

Trace的结果还是很清楚的。1+1起初位于f内部并未计算,它是等到被传递到downvalue的rule的右侧后才开始计算的。

如果到了这一步还觉得没法接受,那么大半是没摆脱语义式(semantic)匹配的思维的影响。总之记住Mathematica核心语言的行为是语法式(syntax)的,而它并没有鉴别一个表达式来自何处的机制……到了这一步一般也都明白了吧。不明白的话请评论。

用户: xzczd (2.2k 分)
采纳于 用户:wonderlands0
Trace就是好。

“Mathematica文档的“函数”指的到底是什么?”
嗯,期待。昨天看了happyfish的例子我也觉得应该是某些概念和通常数学里的同名概念不大一样,我想到的是自变量。
有道理,谢谢了!

我昨天的结论就是:MMA里的函数就像是个吃东西的机器,当自变量被喂到函数的嘴里之前时,它一直保持自变量的身份;然而一旦自变量被喂到函数的嘴里(即被代入进函数),它就不再是自变量了。所以,既然自变量本身已经消失了,那个保持自变量不计算的限定也就不再有任何意义了。

你设为粗体字的那句话我想就是我上面这个意思的更精确的表述。

所以,MMA里“自变量”的概念和数学中“自变量”的概念是有明显差别的。数学里的自变量,当它被代入到函数中以后仍然保持自变量的身份。比如,如果f=(#^2)&,那代入一个参数a后,a^2中的a仍然是自变量。

那么,HoldAll的定义中如果使用数学中“自变量”的意义,就是有纰漏的。但MMA似乎对此概念差别并没有做区分。

对于还不完全懂这些的人来说,此沟在阅读HoldAll的定义时不容易智能跳过。
说到底还是那句话,“Mathematica是编程语言”,它只是类似传统数学表达式,并非传统数学表达式。此外需要强调的是,Mathematica函数的这种行为,或者说,Mathematica对函数做出的这种“限定”,作为编程语言是必要的,你可以想象一下,如果Mathematica使用的是你说的那种函数定义会有多混乱。
嗯。如果能在用词上有所区别似乎更好。
+5 投票

楼上xzczd说的挺好。

实际上只要理解Hold函数的作用那问题就很清楚了。Hold类属性不是说保持一个表达式不计算,而是计算顺序先后的问题。

一个函数涉及到2部分计算,一部分是输入参数是否需要计算(例如f[1+1,2+2]里的加法),第2部分才是这个函数本身定义的算法。那么这两部分的计算,谁先进行谁后进行?不同的计算顺序的计算结果是有区别的。

1.输入参数先计算,然后f再计算。有时候会造成错误意思,首先看看哪些函数具有HoldFirst属性。

Select[Names["System`*"], MemberQ[Attributes[#], HoldFirst] &]
(*{"AddTo", "AppendTo", "AssociateTo", "Catch", "ClearAttributes", \
"Context", "Control", "CreateScheduledTask", "Debug", "Decrement", \
"DialogReturn", "DisplayWithRef", "DivideBy", "Dynamic", \
"Inactivate", "Inactive", "Increment", "KeyDropFrom", "Message", \
"MessageName", "MessagePacket", "NCache", "Pattern", "PreDecrement", \
"PreIncrement", "PrependTo", "Reap", "RefBox", "Refresh", \
"RepeatedTiming", "RuleCondition", "RunScheduledTask", "Set", \
"SetAttributes", "Stack", "SubtractFrom", "TableViewBox", "TimesBy", \
"ToEntity", "Unset", "UpSet", "WaitUntil"}*)

其中AppendTo为什么必须要有HoldFirst属性,我想是很显然的,因为AppendTo的用法就是AppendTo[a,3],其中a={1,2}。如果先计算输入参数,那么AppendTo[a,3]然后就等于AppendTo[{1,2},3],然后就和a无关了,AppendTo内部就无法修改a了。

顺带提一句,And函数具有HoldAll属性,这是十分必要的,想想一下如果没有这个属性,那么And[1>2,4>3]首先被计算成And[False,True],最后得到False,但是实际上我们计算完1>2后就没有必要计算4>3了,所以正是因为And具有HoldAll属性,所以And[1>2,4>3]的Trace里是没有包含4>3的计算的。那么问题来了,And的使用方法仅仅是And[1>2,4>3],不接受And[{1 > 2, 4 > 3}],而有时候我们的判断条件很多,例如用Table生成1000个判断条件,如果用And@@Table[],Apply是不具有Holds属性的,会把每个判断条件都计算。怎么处理这种情况,那么就是另外一个问题了。

2.输入参数不计算,统统套入f后再计算。有时候会造成重复计算。例如

SetAttributes[f, HoldAll];
f[x_] := {x^2, x^3, x^4};
f[1 + 1] // Trace

1+1被计算了3次,因为f的定义里出现了3次x。那按照典型的函数式编程,函数套函数?

SetAttributes[getMaxAndMin, HoldAll];
getMaxAndMin[x_] := {Max[x], Min[x]};
getMaxAndMin[RandomReal[1, 2]] // Trace

然后就崩了。

用户: 苹果 (2.2k 分)
修改于 用户:苹果
谢谢解释!以简单清晰的逻辑说明了那些概念存在的必要性。:)
0 投票

我感觉HoldFirst 和HoldAll的定义里应该加上几个字更好,变为这样:

“在执行函数头部本身的计算之前,某某某自变量保持不计算。”

用户: wonderlands0 (451 分)
你这句话歧义更深,不,大概根本没人会如你所想的去理解吧……试试这个:(1 + 1)[2 + 2] // Trace
喔,竟然还有这样的。。。
不过我相信应该有比现有文档中的定义更好的定义。毕竟定义是给还不太懂的人看的。而已经弄懂的人,确实不会在意定义如何模糊,我现在就觉得无所谓了。
...