为什么b=a; f[a_]:=b; f[2]不输出2?——说说显式存在的重要性
mathematica吧
全部回复
仅看楼主
吧务
level 15
xzcyr 楼主
为方便修订,文章放在了云笔记上,请大家多点一下鼠标。有什么意见或建议欢迎提出:
http://note.youdao.com/noteshare?id=a9c14381c11b44ad47980c44c200d529
2016年12月03日 13点12分 1
吧务
level 12
很多细节以前没注意,学习了!
一点小问题,代码2 /. 1 -> a中1前面的空格可能上传时被吃掉了,建议加上括号以免歧义。
2016年12月04日 05点12分 3
感谢指出,已修正。(话说我都不知道云笔记还会自动排版的啊?)
2017年01月07日 05点01分
level 7
个人认为该说法欠妥
单就title提到的问题, 并不是显式存在的问题, 应该是SetDelayed执行机制问题
我们先看一下Set功能Possible Issues第一条描述:
In the presence of global variables, pattern variables may show unexpected behavior
继续看tutorial/ImmediateAndDelayedDefinitions
开宗明义说了:
You may have noticed that there are two different ways to make assignments in the Wolfram Language: lhs=rhs and lhs:=rhs. The basic difference between these forms is when the expression rhs is evaluated. lhs=rhs is an immediate assignment, in which rhs is evaluated at the time when the assignment is made. lhs:=rhs, on the other hand, is a delayed assignment, in which rhs is not evaluated when the assignment is made, but is instead evaluated each time the value of lhs is requested.
Set是定义时计算, SetDelayed是使用时计算
那么我们来看看这两个的本质区别
就拿title问题举例
b = a;
f[a_] := b
f[2]
这个结果是 a
d = c;
g[c_] = d
g[2]
这个结果是 2
没错, 这对新人来说特别具有迷惑性
但是如果你弄懂了上面的本质区别, 就会知道
g[c_]=d => g[c_]=c
??g 也会看到相同的结论
因此在执行g[2]时, 相当于执行了With[{c=2}, c], 注意, 这里的c是临时变量, 并不是对全局变量c进行赋值
同样的, 执行 f[2], 相当于执行了 With[{a=2}, b]
这里的a依然是临时变量a, 并不是b等于的那个a, 所以b的值依然是a, 不是2
综上所述, 理解这个问题最根本的是明白Mathematica里面Pattern的变量不是实际的变量, 而是占位符的名字罢了
如果系统接受过编程训练, 那就是实参和形参的区别, 至于是Pass By Value还是Pass By Reference, 甚至更细分的 Pass By Reference Call By Value那就不是本帖讨论的范畴了, 具体可以参考维基百科, https://en.wikipedia.org/wiki/Pass_by_reference
2016年12月05日 04点12分 4
……等一下,仔细想想,其实你这个解释也是一个类似于“显式存在”的解释,因为在引入“内外”概念的那一刻,其实就是隐含地使用了“明确地存在于某处”或者说“在字面上存在于某处”的概念了……
2017年01月07日 07点01分
……恕我直言,我觉得你这解释只是用一个同级别问题代替了原问题。认为函数定义执行过程相当于With没啥问题(应该说至少我想不出两者的行为有啥差别),可然后呢,为什么With认不出b里面藏着的a呢?这个问题不引入“显式存在”或者类似的概念是很难说清楚的。顶楼的文章我已写完,欢迎看看。
2017年01月06日 16点01分
@xzcyr 说的很对, 为什么b认不出来a呢? 恰恰是因为这里的a并不是b对应的a, 两个a的scope都不同, 只是名字相同, 外部的a被内部的a shadow了, 所以会返回b的值, 亦即外部的a, 而不是说把内部的a的值赋值给b然后返回2.
2017年01月07日 05点01分
@guocong89 可是如果用作用域来解释的话,要怎么解释Clear[a,b];b=a;f[a_]:=Evaluate[b];f[2]的行为呢?
2017年01月07日 06点01分
level 7
如果非要说是"显式存在", 那当然是没问题, 因为Pattern的执行就是显式匹配, 不是值匹配, 这么理解当然没问题
但我觉得强行往显式存在上靠是不合理的, 因为绝大部分语言在处理参数时都是按照名字去匹配的, 也就是所谓的显式存在.
比如来段c代码
#include <stdio.h>
int a;
int b;
void f(int a)
{
printf("%d\n", b);
}
int main(void)
{
a = 10;
b = a;
f(5);
return 0;
}
虽然概念上稍微有些不同, 但我觉得原理是一致的
外面我们定义了b=a=10
传参我们让a=5
但为什么输出的时候b=10而不是5呢?
明明b=a啊
因为参数a并不是外部的a, 两者是完全没有关系的变量
同样的, f[a_]:=b
a就是一个参数名, 和外面定义的b=a完全没有关系, 这点是承认了吧
所以就是一个编程语言里最最普通的实参形参的问题, 没必要去和显式存在联系起来
至于DownValue, HoldFirst, HoldAll的问题, 那只是一个计算优先级的问题而已, 无非是影响了
f[a_]:=XXXX(b) 是否优先计算XXXX(b)而已, 虽然我觉得整个Hold系统设计的很糟糕, 要不然也不会新开发出Active系统了
如果优先计算了XXXX(b), 那就变成了 f[a_]->a
没有优先计算, 那就是 f[a_]->b
仅此而已
2017年01月07日 06点01分 9
我觉得这段C语言的例子并不合适,因为b在b=a;执行完的那一刻起里面存的就不是a而是10,这和Mathematica的情况并不一样。
2017年01月07日 06点01分
@xzcyr 是的, 因为c语言不存在引用这个概念. 本想写c++的, 用引用表示, 一时手懒没写
2017年01月07日 06点01分
level 7
或者来点可能更暴力的解释, 虽然不太严谨
情况1:
a=10
f[a_]:=a
f[5]
这里三个a分别命名为 a1, a2, a3
不论是用显式存在理论还是参数作用域理论
a2<=>a3, a2和a1没有任何关系
因此, 当我们把a2用5替代时, a3=5, 于是输出了5
情况2:
a=10
b=a
f[a_]:=b
f[5]
这里依然是 a1, a2, a3
同时有 b1, b2
显然 a1<=>a2, a3和a1没有任何关系
因为b2没有在f参数表里出现, 所以不会被localize, 依然是全局的b1, 所以b2<=>b1
那么执行f[5]时, a3被5替代
但是a1=a2=10
b2=b1=a2
所以输出10就一目了然了
2017年01月07日 06点01分 10
level 7
不上c++的代码, 这里就和Mathematica的b=a等价了
#include <iostream>
using namespace std;
int a = 10;
int &b = a;
void f(int a)
{
cout<<b<<endl;
}
int main()
{
a = 20;
f(5);
return 0;
}
2017年01月07日 06点01分 11
其实说它们“等价”不太准确,因为C的指针应该是直接指向了a的值所在的地址,而Mma的b:=a; b的最后一步其实是通过b里面的Ownvalue(又是模式匹配……当然,似乎是比较特殊的一种:http://mathematica.stackexchange.com/q/134786/1871)找到a头上然后再执行a。(事实上:=甚至不能编译…)(接下条
2017年01月07日 07点01分
不过单就本帖讨论的内容来说,这段代码的行为确实和Mathematica没区别(大概)。
2017年01月07日 07点01分
1