找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

[研讨] 型参和实参相同,有什么副作用?

[复制链接]
发表于 2014-11-17 13:45:12 | 显示全部楼层
/db_自贡黄明儒_ 发表于 2014-11-17 12:09
set函数历来是个难点,看来你驾轻就熟

在 AutoLISP  中,变量名和值是统一的,这个 指针、地址概念不同,自定义函数中参数传递的是 “值”,对应其它语言的 “地址”,看看下面的例子
  1. (defun swap (var1 var2 / var3)
  2.   (setq var3 (vl-symbol-value var2))
  3.   (set var2 (vl-symbol-value var1))
  4.   (set var1 var3)
  5.   t
  6. )

_$ (setq l1 '(1 2 3 4))
(1 2 3 4)
_$ (setq l2 '(a b c d))
(A B C D)
_$ (swap 'l1 'l2)
T
_$ l1
(A B C D)
_$ l2
(1 2 3 4)
_$

在 XD::DCL 部分函数中大量这种用法
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 8121个

财富等级: 富甲天下

发表于 2014-11-17 15:00:50 | 显示全部楼层
本帖最后由 Highflybird 于 2014-11-17 15:05 编辑

实际上,你这个不叫形参与实参相同,应该叫做 局部变量名与形参相同。
也许你看到高手有时候把这两个变量名定义相同,为的是节省一个局部变量名字。更多的作用我是没看见有何用途。
对于初学者来说,不建议这么做。这样做的结果,很可能是他自己不清楚这个参数在函数结束后,是不是真的返回原来的数值呢?
好,下面来做一个实验:
(defun change (point)
  (vlax-safearray-put-element point 0 100)
  (vlax-safearray-put-element point 1 100)
  (vlax-safearray-put-element point 2 0)
  point
)

(setq point (vlax-make-safearray vlax-vbDouble '(0 . 2)))
这个时候 point的坐标应该是 (0 0 0)
运行函数 (change point)
那么这个时候的坐标是多少呢?你是否以为还是(0 0 0)
错了,这时候的坐标 (100 100 0)了。

再看另外的一个例子:
(defun foo1 (x)
  (setq x 2)
)

(defun foo2 (&x)
  (set &x 2)
)

  (setq x 1)          ==> x=1
  (foo1 x)  运行后 ==> x=1     
  (princ x)         
  (foo2 'x) 运行后 ==> x=2    
  (princ x)

可见,x的数值发生了变化。为何,因为我们传了了一个' 相当于引用,或者传址。
所以,对于LISP的参数如果是一个引用、数组或者 vla-object的时候,这时候就可能在函数运行完后,改变该参数的值。

对于初学者来说,有些代码还是老老实实地好。



点评

不错,不加 ‘ 传递的是值,最常用的判断就是 type ,如果加 ' 传递的是变量名,用 type 就是 SYM ,如果使用其值,就需要使用 vl-symbol-value  详情 回复 发表于 2014-11-17 15:26
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

发表于 2014-11-17 15:26:31 | 显示全部楼层
Highflybird 发表于 2014-11-17 15:00
实际上,你这个不叫形参与实参相同,应该叫做 局部变量名与形参相同。
也许你看到高手有时候把这两个变量 ...

不错,不加 ‘ 传递的是值,最常用的判断就是  type ,如果加 ' 传递的是变量名,用 type 就是 SYM ,如果使用其值,就需要使用  vl-symbol-value



论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 6530个

财富等级: 富甲天下

发表于 2014-11-17 16:08:09 | 显示全部楼层
Highflybird 发表于 2014-11-17 15:00
实际上,你这个不叫形参与实参相同,应该叫做 局部变量名与形参相同。
也许你看到高手有时候把这两个变量 ...

在传统的ALisp中,参数有点局部参数的意思,就是参数使用是变量名的值在函数运算过程中是不变的,就像foo1中的参数x,即使在系统函数中,也只有少数的函数可以改变参数的内容(选择集函数、set族和defun族),这可能与早期的设计思想有关,在《On_Lisp》中就有一个例子bed_reverse,因为(bed_reverse lst)直接改变了lst的值而被作为反面典型,书中的结论是“函数如其名,bad-reverse 与好的Lisp 风格相去甚远。更糟糕的是,它还有其它丑陋之处:因为其正常工作有赖于副作用,所以它使调用者离函数式编程的理想渐行渐远”。
在函数foo2中传递了函数名,严格意义上还不是改变了参数的值,对函数来说,参数是'x,改变的是x,这其实还不是改变参数的值。
但函数的change的的确确是在改变参数,这和传统的“好”不同,但这样做一定有其自己的用意,没有研究VL,不好评价,但可以肯定,这样的用法会给编程者(尤其是初学者)带来一定的困惑,就像你所举的例子。

点评

ll_j 说的很对: 在函数foo2中传递了函数名,严格意义上还不是改变了参数的值。 帮助大家理解一下这个foo2函数: (defun foo2 (&x) (set &x 2) ) 在这里传入的参数是符号, &x代表符号 函数中 对  详情 回复 发表于 2014-11-17 21:17
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 1268个

财富等级: 财源广进

发表于 2014-11-17 16:36:22 | 显示全部楼层
某些时候使用这个 "反传统"  方法会有特殊功效,需要实践去积累

点评

对,有时候运用这一点,可以有想不到的功效;譬如,交换两个变量的数值,如果用以下函数: (defun swap_byVal (x y / z) (setq z y) (setq y x) (setq x z) ) 假设 x=1,y=2; 运行 (swap_byVal x  详情 回复 发表于 2014-11-17 17:18
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 8121个

财富等级: 富甲天下

发表于 2014-11-17 17:18:43 | 显示全部楼层
本帖最后由 Highflybird 于 2014-11-17 17:44 编辑
st788796 发表于 2014-11-17 16:36
某些时候使用这个 "反传统"  方法会有特殊功效,需要实践去积累

对,有时候运用这一点,可以有想不到的功效;譬如,交换两个变量的数值,如果用以下函数:
(defun swap_byVal (x y / z)
   (setq z y)
   (setq y x)
   (setq x z)
)
假设 x=1,y=2;
运行 (swap_byVal  x y)之后,实际上x,y并没交换,x还是为1,y还是为2;
如果这样定义:
(defun swap_byRef (&x &y / z)
   (setq z  (eval y))
   (set &y (eval &x))
   (set &x  z)
)

运行 (swap_byRef 'x 'y)之后
发现x和y是真正交换了。x=2,y=1.
在某些情况下,我们是可以这样用的。

如果我们想在函数结束后,返回多个值,就可以用这个方法。
例如下面的函数:根据边长,求面积,体积,周长。
  1. (defun nRet (l &area  &perimeter &volume)
  2.   (set &area  (* l l))
  3.   (set &perimeter (* 4 l))
  4.   (set &volume (* l l l))
  5. )

然后调用  $ (nRet 2.5 's 'p 'v)
_$ s   面积
6.25
_$ p   周长
10.0
_$ v   体积
15.625
这样一来,就一下子得到了三个值,如果用传统的方法,估计要返回一个表,从这个表中提取数值,然后再赋值。现在这个办法就简洁多了,因为它把赋值的过程都在函数里面完成了。


论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 1268个

财富等级: 财源广进

发表于 2014-11-17 17:27:29 | 显示全部楼层
Highflybird 发表于 2014-11-17 17:18
对,有时候运用这一点,可以有想不到的功效;譬如,交换两个变量的数值,如果用以下函数:
(defun swap_ ...

"副作用"  变 "正能量" {:soso_e113:}

点评

刚刚测试了一下,似乎VL-SYMBOL-VALUE 比 eval 快了一些. Statement Times Elapse(ms) Average(ms/time) ----------------------------------------------------------------------  详情 回复 发表于 2014-11-17 18:41
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 8121个

财富等级: 富甲天下

发表于 2014-11-17 18:41:07 | 显示全部楼层
st788796 发表于 2014-11-17 17:27
"副作用"  变 "正能量"

刚刚测试了一下,似乎VL-SYMBOL-VALUE eval 快了一些.
Statement                         Times    Elapse(ms)    Average(ms/time)
-------------------------------------------------------------------------
VL-SYMBOL-VALUE           1000000  7301.0        0.007301
VL-SYMBOL-VALUE           1000000  7348.0        0.007348
EVAL                              1000000  12199.0       0.012199
EVAL                              1000000  12230.0       0.01223         
(("VL-SYMBOL-VALUE" 1000000 7301.0 0.007301 (1 2 3))
("VL-SYMBOL-VALUE" 1000000 7348.0 0.007348 1)
("EVAL" 1000000 12199.0 0.012199 1)
("EVAL" 1000000 12230.0 0.01223 (1 2 3)))

点评

两者使用范围不一样,eval 后面可以是表达式,vl-symbol-value 后面只能是 SYM ,可能 eval 要处理的情况更多  详情 回复 发表于 2014-11-17 19:23
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 859个

财富等级: 财运亨通

发表于 2014-11-17 19:23:03 来自手机 | 显示全部楼层
Highflybird 发表于 2014-11-17 18:41
刚刚测试了一下,似乎VL-SYMBOL-VALUE 比 eval 快了一些.
Statement                         Times     ...

两者使用范围不一样,eval 后面可以是表达式,vl-symbol-value 后面只能是 SYM ,可能 eval 要处理的情况更多
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 8121个

财富等级: 富甲天下

发表于 2014-11-17 20:40:35 | 显示全部楼层
当然还有一个副作用就是:如果你把这个变量名弄成相同了,你就覆盖了原来的值
那么如果程序运行中改变了值,你就无法得到传入时候的值了,只有函数结束后,才能恢复。
所以用的时候要小心。
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 8121个

财富等级: 富甲天下

发表于 2014-11-17 21:17:38 | 显示全部楼层
本帖最后由 Highflybird 于 2014-11-17 21:20 编辑

[quote]ll_j 发表于 2014-11-17 16:08
ll_j 说的很对: 在函数foo2中传递了函数名,严格意义上还不是改变了参数的值。
帮助大家理解一下这个foo2函数:

(defun foo2 (&x)
  (set   &x 2)
)

在这里传入的参数是符号, &x代表符号
函数中 对 &x赋值。  
(set  &x 2)
回想一下Set函数,set 函数与 setq 类似,但 set 计算两个参数的值。
所以:
(foo2  'x)  意味着对x赋值, 哪怕以前从未对x赋值,这时候x也赋值了。
(foo2  'y)   ==> y =2;
但是 (foo2 '&x)   又是什么结果呢?
(foo2 '&x)后的结果 是    &x ==>nil   !!!
所以变量同名的时候,大家要小心。

点评

在同一生命周期,名必须唯一这是任何语言都要遵守的规则吧  详情 回复 发表于 2014-11-17 21:45

评分

参与人数 1D豆 +5 收起 理由
/db_自贡黄明儒_ + 5 幸好我很少用set,避免了犯错误的可能^_^

查看全部评分

论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 264个

财富等级: 日进斗金

发表于 2014-11-17 21:45:02 来自手机 | 显示全部楼层
Highflybird 发表于 2014-11-17 21:17
[quote]ll_j 发表于 2014-11-17 16:08
ll_j 说的很对: 在函数foo2中传递了函数名,严格意义上还不是改变 ...


在同一生命周期,名唯一这是任何语言都要遵守的规则吧
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|申请友链|Archiver|手机版|小黑屋|辽公网安备|晓东CAD家园 ( 辽ICP备15016793号 )

GMT+8, 2024-9-24 16:32 , Processed in 0.209032 second(s), 48 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表