明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 2288|回复: 17

[提问] 关于全局变量的定义问题

[复制链接]
发表于 2015-9-4 18:29:43 | 显示全部楼层 |阅读模式
本帖最后由 Gu_xl 于 2015-9-5 08:59 编辑

在一个lisp文件里,有多个程序defun,对于全局变量不知道具体应该如何处理。原代码如下:
  1. (vl-load-com)
  2. (setvar "CMDECHO" 0)

  3. ;这个函数是用来取得图纸上所有的图层名组成的集合
  4. (defun ALL-LAY  (/ LAY I)
  5.   (vlax-for I  (vla-get-layers
  6.      (vla-get-activedocument (vlax-get-acad-object)))
  7.     (setq LAY (cons (vla-get-name I) LAY)))
  8.   (setq LAY (vl-sort LAY '<)))

  9.   ;这个函数是用来取得选择集合里所包含的所有图层名集合,且图层名集合不重名
  10.   (defun S-LAY  (/ SS I LAY)
  11.   (setq SS (ssget))
  12.   (setq  SS (vla-get-activeselectionset
  13.        (vla-get-activedocument (vlax-get-acad-object))))
  14.   (vlax-for I SS (setq LAY (cons (vla-get-layer I) LAY)))
  15.   (setq LAY (vl-sort LAY '<))); 不重名设置,同时作为函数的返回值
  16.   
  17. ;这个函数是用来取得除了选择集以外的所有图层名集合
  18. (defun NS-LAY  ( / LAY lays MBR)
  19.   (setq LAY (ALL-LAY))
  20.   (setq lays (S-LAY))
  21.   (setq clay (car lays))
  22.   (foreach MBR lays (setq LAY (vl-remove MBR LAY)))
  23.   (setq LAY (vl-sort LAY '<)))

  24. ;打开关闭系列
  25. (defun C:1  ()
  26.   (prompt "图层全开.\n")
  27.   (command "_.-LAYER" "ON" "*" "")
  28.   (princ))

  29.   (defun C:2   ( / LAY MBR )
  30.   (prompt "关闭其他.\n")
  31.   (setq lay (NS-LAY))
  32.   (if (vl-position (getvar "CLAYER") lay);判断表中是否有当前图层,有的话关闭当前图层
  33.     (command "_.-LAYER" "OFF" (getvar "CLAYER") "Y" ""))
  34.   (setq LAY (vl-remove (getvar "CLAYER") lay))
  35.   (command "UNDO" "BE")
  36.   (foreach MBR LAY (command "_.-LAYER" "OFF" (eval MBR) ""))
  37.   (setvar "CLAYER" clay )
  38.   (setq clay nil )
  39.   (command "UNDO" "E")
  40.   (princ))
  41.   
  42. (defun C:3  (/ LAY MBR)
  43.   (prompt "关闭自己.\n")
  44.   (setq lay (S-LAY))
  45.   (if (vl-position (getvar "CLAYER") lay);判断表中是否有当前图层,有的话关闭当前图层
  46.     (command "_.-LAYER" "OFF" (getvar "CLAYER") "Y" ""))
  47.   (setq LAY (vl-remove (getvar "CLAYER") LAY))
  48.   (command "UNDO" "BE")
  49.   (foreach MBR LAY (command "_.-LAYER" "OFF" (eval MBR) ""))
  50.   (command "UNDO" "E")
  51.   (princ))
这个Lisp文件中,主要的疑问在于函数 (defun C:2   ( / LAY MBR )...)调用了函数(defun NS-LAY  ( / LAY lays MBR)...),这两个函数里都有一个变量clay,我希望这个变量是全局变量,这样就可以传递值。

但当我将这两个函数改成 (defun C:2   (clay  / LAY MBR )...)和(defun NS-LAY  ( clay / LAY lays MBR)...)时,程序就断了,断的地方就是定义这里。

局部变量名称前的斜线和第一个局部名称之间,以及该斜线和最后一个参数(如果存在的话)之间,都必须保持至少一个空格的距离。这一点我有注意,但我不知道哪里出错了,一直找不到原因。

当我把clay全局变量取消,不去定义时,程序对于不定义的变更默认成全局变量,程序又可以正常运行。所以想请教一下哪里出了错,谢谢~



发表于 2015-9-4 21:29:22 | 显示全部楼层
简单阐述下,函数定义的时候 / 之后的都是局部变量,表示只在该程序体内有效,运行完这个函数就失效了。
而且局部变量不影响全局变量。如果该函数定义中有未加入/ 之后的变量,就是全局变量,作用于当前文档。。。
===================
现在说说函数的调用与变量之间的关系。
函数也分全局函数和局部函数。如果是局部函数基本上等于局部变量的关系,将局部函数写入/之后,那么函数定义只在该主函数之内有效,否则是全局函数。

那么主函数和子函数我们调用的时候,变量的关系怎么定位呢。
按你的例子,定义1个 c:2的命令函数,在2这个函数体内调用了NS-LAY 这个子函数,
那么关于子函数的变量调用有两种,一种是参数调用,一种是全局变量调用,
关于这个你可以在论坛找找资料。
说说你的问题。
(defun C:2   (clay  / LAY MBR )...)你这里的命令函数也采用了参数,所以可能是这里的问题。
命令函数加上参数的话,就等于需要另外一个命令来调用,并用(c:2 "clay值")的方式调用。才能实现2这个命令。
因此一般命令函数是不加参数的。还是用这个 (defun C:2   ( / LAY MBR )...)
子函数是参数调用(defun NS-LAY  ( clay / LAY lays MBR)...)
你在c:2中调用的时候应该是(NS-LAY "clay 参数值或变量")

如果你只想clay作用于该命令之中那么主函数将clay定义为局部函数即可。
如果clay是带有记忆功能,其他的命令函数都要调用,那么不用定义为局部变量,就等于是全局变量,在该文档的任何程序都能读取到clay值。
发表于 2015-9-4 21:36:53 | 显示全部楼层
edata 发表于 2015-9-4 21:29
简单阐述下,函数定义的时候 / 之后的都是局部变量,表示只在该程序体内有效,运行完这个函数就失效了。
而 ...


好象定义太多局部函数的话有可能编译后出错。。。
发表于 2015-9-4 22:17:17 | 显示全部楼层
llsheng_73 发表于 2015-9-4 21:36
好象定义太多局部函数的话有可能编译后出错。。。

很少编译,这个不太了解了,改天有空了试试,局部函数的优势应该在于不会与全局函数冲突,缺点是需要多次加载,对于某些命令我们不太会重复调用的情况下,可以考虑局部函数,如果多次调用,还是全局函数有优势。。
 楼主| 发表于 2015-9-4 22:33:20 | 显示全部楼层
edata 发表于 2015-9-4 21:29
简单阐述下,函数定义的时候 / 之后的都是局部变量,表示只在该程序体内有效,运行完这个函数就失效了。
而 ...

我明白自己错在哪里了。
  1. (defun add10(x)
  2.     (setq  x(+  10  x))
  3. )
就像这个函数一样,我要调用的话,就必需(add10(3)),这里的全局变量是用来外部传入数值到本函数,和其它语言是一样的。

我之前一直以为函数(aaa / bbb)全局变量和局部变量都只是定义作用,只是用来表达aaa是全局变量,bbb是局部变量。

现在更正了这个观点后(aaa / bbb)这里,aaa是需要由外部传入数据到调用的函数里,aaa将是一个具体的值。但bbb不是一个具体的值,也不需要任何数据传入,这样理解是否有误 ?
如以下函数:
  1.   (defun volume(r  h  /  v)
  2.       (setq  v(*  r  r  pi  h))
  3.   )
我要调用的话,应该就是(volume(3 2)),而不应该是(volume(3 2 / 10))。

那么关于子函数的变量调用有两种,一种是参数调用,一种是全局变量调用,关于这一点,我应该搜索的关键词是"子函数的变量调用"吗?没有找到答案。以下是我的理解,不知道是否有错:
在主函数里调用子函数add10(x)时,先(setq a 3),再(add10(a)),这样是否就叫作参数调用 ?
如果将原子函数(defun add10(x)...)更改成(defun add10()...)后,我要调用add10()时,在主函数里将x定义成全局变量并给值,先(setq x 3),再(add10()),这样是否就是全局变量调用 ?
发表于 2015-9-4 22:39:45 | 显示全部楼层
(add10(a))应该是(add10 a)
如果你定义的函数为(defun volume(r  h  /  v),调用的时候 (volume r  h )而不是 (volume(3 2))

  1. (setq a 999 b 1000) ;全局变量 a b
  2. (defun c:tt(/ a b c);局部变量 a b c
  3.   (print (list 0 a b));检查ab值发现为nil因为在tt函数中,ab值还没有定义
  4.   (setq a 1)
  5.   (setq b 2)
  6.   (setq c 3)
  7.   (sk_test2);调用子函数
  8.   (print (list 1 a b c));检查 abc的值(ab值不会改变是12不受子函数影响,但是c在子函数中是全局变量所以影响了tt函数的c值)
  9.   )
  10. (defun sk_test2(/ a b );定义ab为子函数的局部变量,未定义c,所以c在此时作为了tt函数内的全局变量
  11.   (setq a 6)
  12.   (setq b 10)  
  13.   (print (list 2 a b c));检查子函数的abc值,发现ab值为子函数定义的值,c为tt函数内的全局变量值
  14.   (setq c 15);变更c的值,因为在子函数中是全局变量,所以,c值被改变了。
  15.   )
  16. (print (list 3 a b c));在执行完tt之后再运行这句检查abc可以发现ab为全局变量值,不是tt也不是子函数的ab值,c值因为不属于全局变量所以为nil
发表于 2015-9-4 22:59:55 | 显示全部楼层
函数调用的时候 (sk_add a b c)其中abc必须存在(不在乎是否有值),不然不能调用,(sk_add nil nil nil)都能调用,这句必须是这种格式(函数名 参数1 参数2 。。。)至于参数是nil都不影响调用,
当然如果(sk_add nil nil nil)那么在sk_add计算的时候,就会出错了。如果 sk_add不检查的话。
  1. (defun c:tt(/ a b c)
  2.   (setq a 10 b 20 c 30)
  3.   (print (cons "加" (sk_add a b c)))
  4.   (print (cons "减" (sk_dec a b c)))
  5.   (princ)
  6.   )
  7. (defun sk_add(d e f / a b c) ;def为该函数的参数,abc为局部变量
  8.   (setq a 5 b 6 c 7)
  9.   (print (list a b c))
  10.   (print (list d e f))
  11.   (mapcar '+ (list a b c) (list d e f))
  12.   )
  13. (defun sk_dec(a b c / d e f);abc为该函数的参数,def为局部变量
  14.   (setq d 5 e 6 f 7)
  15.   (print (list a b c))
  16.   (print (list d e f))
  17.   (mapcar '- (list a b c) (list d e f))
  18.   )
发表于 2015-9-4 23:49:38 | 显示全部楼层
本帖最后由 llsheng_73 于 2015-9-4 23:55 编辑
BenjaminXM 发表于 2015-9-4 22:33
我明白自己错在哪里了。就像这个函数一样,我要调用的话,就必需(add10(3)),这里的全局变量是用来外部传 ...


(defun volumn(r h / v)
  ...)
这里只有v是局部变量,r,h都不是全局变量,它们是这个函数的入口参数。
如果除了入口参数以外需要用到变量,而这个变量又没有在/后边出现,那么这个变量自然就成为了局部变量。。。。。关于全局和局部,lisp和很多语言不一样,很有语言都会要求明确的定义哪些函数或者变量是全局的或者局部的,lisp没有这种要求,但会进行区分,依据就是它是否出现在/后边
入口参数是无法与全局变量混为一谈的,你不可能先通过(setq r 10 h 5)然后直接(volumn)....这样会因为参数不足而无法实现函数调用,而不是你所理解的那样,你认为/前边的是全局变量,所以你觉得这样是可行的,但事实不是这么回事。。。
 楼主| 发表于 2015-9-5 01:07:08 | 显示全部楼层
llsheng_73 发表于 2015-9-4 23:49
(defun volumn(r h / v)
  ...)
这里只有v是局部变量,r,h都不是全局变量,它们是这个函数的入口参数 ...

谢谢,已经明白了。
入口参数,这四个字很直观的说明问题了。
谢谢~
 楼主| 发表于 2015-9-5 01:16:50 | 显示全部楼层
edata 发表于 2015-9-4 22:39
(add10(a))应该是(add10 a)
如果你定义的函数为(defun volume(r  h  /  v),调用的时候 (volume r  h ) ...

(add10 a)和(volume 3 2),写错了,谢谢更正。

下面附的代码有点疑问:
1. sk_,这个代表的意思是?我初步判断应该是你写子程序的时候都会加上这个前缀,前缀代表的意思是?
2. (0 nil nil),(2 6 10 3),(1 1 2 15)最后一个结果的c值是15,这里有点乱了。本来主程序tt里的c是局部变量,现在因为调用了子程序sk_test2后,因为子程序中c是全局变量,所以也导致了主程序tt里的c也相应变成了全局变量,是这个意思吗?
您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|CAD论坛|CAD教程|CAD下载|联系我们|关于明经|明经通道 ( 粤ICP备05003914号 )  
©2000-2023 明经通道 版权所有 本站代码,在未取得本站及作者授权的情况下,不得用于商业用途

GMT+8, 2025-7-16 14:45 , Processed in 0.269264 second(s), 27 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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