找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 4597|回复: 20

[研讨] 如何能在本函数内实现return的效果或者goto的效果呢?

[复制链接]

已领礼包: 8121个

财富等级: 富甲天下

发表于 2013-5-8 21:33:41 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
我们知道,C语言有return 关键字,return的效果是,当语句读到这个return时,函数或者程序就跳出所有语句,然后返回return语句后面的值,函数终结。而goto则是无条件转移到指定语句。
在lisp中,语句如果是层层嵌套的,括号内的语句没完,是不会跳转的,如何能像其他语言那样,在本函数内实现return的效果或者goto的效果呢?
我在lisp中测试了一下,发现很难实现这两种效果!
下面是我的例子:
[pcode=lisp,true]
(defun fff (x jump)                       ; x这个参数可有可无
  (cond
    ((= jump 10)
      (setq return 31)
    )
    ((= jump 20)
      (setq return 21)
    )
    ((= jump 30)
      (setq return z)
    )
    (t
      (setq i 0)
      (while (< i 50)
        (setq x 1)
        (if (= i 10)                       ; 本想跳出while循环的,直接返回值
          (setq jz 10
                return (fff x jz)
          )
        )
        (setq y 2)
        (if (= jump 20)                       ; 没办法跳出去
          (setq jz 20
                return (fff x jz)
          )
        )
        (setq z 31)
        (if (= z 31)                       ; 没办法跳出去
          (setq jz 30
                return (fff x jz)
          )
        )
        (setq i (1+ i))
      )
      (setq return 100)
    )
  )
)

[/pcode]


各位想想办法!


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

已领礼包: 6530个

财富等级: 富甲天下

发表于 2013-5-8 21:59:19 | 显示全部楼层
以我对“表”结构的理解,return是和“子函数”调用相近的,但goto的结构在“表”结构中是无法完全模拟的,对非表结构的语言而言,进出通道是多向的,可以任意“跳转”,但表结构则不同,单进单出,在一个闭括号前,即使引用了一个子函数,仍旧只是嵌套,上一层函数的运行仍旧没完,需要时只是做一个“标记”,临时封闭一下,不会交叉,也正是这个原因,所以,在Lisp中使用太多层次的递归是有“风险”的。
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 51个

财富等级: 招财进宝

发表于 2013-5-8 22:12:00 | 显示全部楼层

我的看法是,递归是一个很“成熟”的算法,无非是参数入栈,RETURE后参数出栈,只受“栈”空间的限制,递归过程本身并无风险。也不会因为是LISP在用递归就增加出错的可能性。

点评

就是因为表结构的单进单出这种特征,才说Lisp的递归有风险。 递归是调用自己,如果可以goto,则每次调用自己都可以把“指针”直接移动到一个新的位置,但像Lisp这种表结构不同,每次调用都是把前面的东西临时入栈,  详情 回复 发表于 2013-5-8 22:28
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 51个

财富等级: 招财进宝

发表于 2013-5-8 22:23:41 | 显示全部楼层
我认为要想在WHILE循环里面跳出的话,应该在:

  1.      (while (and 条件1 条件2)
  2.           ................  ;;条件1是你本来的逻辑,条件2是你加的T或者NIL,程序中让条件2为NIL,跳出循环并返回值。
  3.      )
  4.   
复制代码
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 6530个

财富等级: 富甲天下

发表于 2013-5-8 22:28:49 | 显示全部楼层
Lispboy 发表于 2013-5-8 22:12
我的看法是,递归是一个很“成熟”的算法,无非是参数入栈,RETURE后参数出栈,只受“栈”空间的限制,递 ...

就是因为表结构的单进单出这种特征,才说Lisp的递归有风险。
递归是调用自己,如果可以goto,则每次调用自己都可以把“指针”直接移动到一个新的位置,但像Lisp这种表结构不同,每次调用都是把前面的东西临时入栈,指针却并没有真正跳出去,不管多少层的递归,最终还要一层一层地出来。
的确如你所说,递归本身并无风险,但“栈”的空间会有限制,这个限制就是一个不良递归在Lisp中的风险。
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 51个

财富等级: 招财进宝

发表于 2013-5-8 22:45:46 | 显示全部楼层
本帖最后由 Lispboy 于 2013-5-8 22:48 编辑
ll_j 发表于 2013-5-8 22:28
就是因为表结构的单进单出这种特征,才说Lisp的递归有风险。
递归是调用自己,如果可以goto,则每次调用 ...

继续探讨,

先不说楼主希望的在循环中加入RETURN还是GOTO,都是在一个递归调用层内部的迭代代码中的跳转,不管怎么转,都是在表函数定义的最后那个括号才结束函数返回上一层,而在递归程序底层核心运行的时候,是安全的入栈和出栈的。

入栈的时候,先入栈程序调用的返回地址,接着入栈参数,出栈的时候是反操作,最后弹出返回地址,同时栈顶指针下移。栈顶指针就像一个“浮漂”。

LISP代码在一层递归中无论如何也不会破坏到栈的结构的,所以也就不存在所谓的风险,即使出错,也不是因为堆栈的错误。仅仅可能是代码的不完善,比如对象的不正确打开和关闭等导致的错误。

“递归”是很老实的运行的,安全的进去安全的出来,至于结果是不是期望的,那就是算法层次的问题了。

我说的受“栈”的空间的限制,是指栈能保存的参数和地址的多少,也就是决定了能递归多少层,早期的汇编语言,机器结构在8位整数的时候,栈空间好像是256K,发展到现在,栈空间应该很大了,具体多少因为好久没碰汇编,不太清楚。我想常归的层次是一点没问题的。

点评

递归是很完善的一种结构,在很多种语言中都有涉及,并且从程序结构和运行效率来说,递归都是很好的,但在AutoLisp语言中,却始终没有提及递归,于是这个问题也是我多年疑惑的问题,只到最近在倒过来理解表结构,才有  详情 回复 发表于 2013-5-8 23:14
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 344个

财富等级: 日进斗金

发表于 2013-5-8 23:04:55 | 显示全部楼层
Lisp语言的特色就是表依次运行,除了while循环和cond条件判断可以跳出外,几乎没别办法!但是Lisp还提供了一个错误处理机制函数*error*,这个 *error*函数还是可以好好利用一下的,我们在需要跳出的地方直接制造一个错误,程序立刻终止了,返回值可以在*error*函数中来处理!
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 51个

财富等级: 招财进宝

发表于 2013-5-8 23:10:55 | 显示全部楼层
牢固 发表于 2013-5-8 23:04
Lisp语言的特色就是表依次运行,除了while循环和cond条件判断可以跳出外,几乎没别办法!但是Lisp还提供了一 ...

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

使用道具 举报

已领礼包: 6530个

财富等级: 富甲天下

发表于 2013-5-8 23:14:25 | 显示全部楼层
Lispboy 发表于 2013-5-8 22:45
继续探讨,

先不说楼主希望的在循环中加入RETURN还是GOTO,都是在一个递归调用层内部的迭代代码中的跳 ...

递归是很完善的一种结构,在很多种语言中都有涉及,并且从程序结构和运行效率来说,递归都是很好的,但在AutoLisp语言中,却始终没有提及递归,于是这个问题也是我多年疑惑的问题,只到最近在倒过来理解表结构,才有这种“风险”的感觉。
现在的系统内存管理和早期已经不可同日而语,栈空间自然也不再是256k,而且,自从电脑系统使用扩展内存(ACADR11开始)以来,栈空间理论上也可能无限制了,但即使这样,内存也还是有限制的。
一般说来,一个不完善的Lisp程序在出错时只会跳出程序运行,返回错误代码,但一个不收敛的递归却并不是一个错误,有时只会陷入一个死循环而不是出错,并且有时还无法人工中断,这种情况的后果是可想而知的,这就是Lisp使用递归的风险。
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 51个

财富等级: 招财进宝

发表于 2013-5-8 23:28:32 | 显示全部楼层
ll_j 发表于 2013-5-8 23:14
递归是很完善的一种结构,在很多种语言中都有涉及,并且从程序结构和运行效率来说,递归都是很好的,但在 ...

看LL_J大师的帖子总能学到东西。

递归仅仅作为一种数据结构的算法,并不是需要每种语言去特意的介绍它,很多介绍它的都是在用来描述数据结构的时候,比如《《数据结构-C++实现》》什么的,这是因为这些语言比如C++,数据类型可以很方便的描述数据的存储结构而已,比如有丰富的指针操作,天然的适合于描述数据结构。

递归和迭代,循环,选择结构等一样,都是一种组织数据的算法。LISP不适合描述它,但并不是LISP不能使用它。就是函数调用自身而已,任何支持函数的语言都能做。

你说的那些出错的可能,可能源于不佳的算法,不佳的操作导致的死循环什么的,原罪不在递归。也不会是因为LISP在层层递归调用、返回的堆栈操作造成的事故,不是LISP语言本身的过失。

反过来,LISP作为表处理语言,天然的适合“递归”,因为CONS和CDR就是天然的入栈和出栈的形象应用,这点欢迎大家讨论。

点评

我也是很早就使用递归了,但有些早期程序现在看起来夸张点说是有点后怕,以前有个程序的递归竟然是出错才退出递归,就是因为结果是正确的,一直到近期重写代码才发现。 程序中使用递归的另一个问题就是检查错误相对  详情 回复 发表于 2013-5-8 23:54
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 344个

财富等级: 日进斗金

发表于 2013-5-8 23:37:01 | 显示全部楼层
本帖最后由 牢固 于 2013-5-8 23:53 编辑
Lispboy 发表于 2013-5-8 23:10
G版的思路不错,不知道是否可行,G版能否给做个例子出来?

[pcode=lisp,true](defun fff (X JUMP )  
  ;;x这个参数可有可无
  (cond
    ((= jump 10) (setq return 31))
    ((= jump 20) (setq return 21))
    ((= jump 30) (setq return z))
    (t
     (setq i 0)
     (while (< i 50)
       (setq x 1)
       (if (= i 10)
         (progn
           (setq return 31)
           (1+ "a") ;_ 制造错误跳出
         ;;本想跳出while循环的,直接返回值
         (setq jZ     10
               return (fff x jZ)
         )
         )
       )
       (setq y 2)
       (if (= jump 20)
         (progn
           (setq return 21)
           (1+ "a") ;_ 制造错误跳出
         ;;没办法跳出去
         (setq jZ     20
               return (fff x jZ)
         )
         )
       )
       (setq z 31)
       (if (= z 31)
         (progn
            (setq return z)
           (1+ "a") ;_ 制造错误跳出
         ;;没办法跳出去
         (setq JZ     30
               return (fff x JZ)
         )
         )
       )
       (setq i (1+ i))
     )
     (setq return 100)
    )
  )
)
;;按如下执行执行函数
(defun c:tt (/ *error*)
    (defun *error* (s)
    (prompt "**提前跳出**")
      ;;打印结果
    (print return)
    ;;还可以些别的处理代码
    )

  (fff  0 2)
  ;;正常执行打印结果
  (print return)
  (princ)
  )

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

使用道具 举报

已领礼包: 51个

财富等级: 招财进宝

发表于 2013-5-8 23:37:54 | 显示全部楼层
牢固 发表于 2013-5-8 23:04
Lisp语言的特色就是表依次运行,除了while循环和cond条件判断可以跳出外,几乎没别办法!但是Lisp还提供了一 ...

我的直观感觉,*error*应该就是直接咔嚓,多少层递归都不返回了,直接结束函数,LISP本身在*error*的时候应该有递归保护,不会让ACAD因为“栈”错误崩溃。不知道G版能不能提出一个*error*仅返回一层的例子来?
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 6530个

财富等级: 富甲天下

发表于 2013-5-8 23:54:27 | 显示全部楼层
Lispboy 发表于 2013-5-8 23:28
看LL_J大师的帖子总能学到东西。

递归仅仅作为一种数据结构的算法,并不是需要每种语言去特意的介绍它 ...

我也是很早就使用递归了,但有些早期程序现在看起来夸张点说是有点后怕,以前有个程序的递归竟然是出错才退出递归,就是因为结果是正确的,一直到近期重写代码才发现。
程序中使用递归的另一个问题就是检查错误相对不太直观,稍复杂时可能容易或略收敛性。
看G版的程序,还是经常会有使用出错代码判别的,但我个人并不太喜欢,我更喜欢对各种情况进行明确的判别。
再回头说主题,其实表结构中任一个子函数的调用,甚至包括任一对括号的执行,都是一个return过程,但goto就不一样了,原则上,使用出错来强制跳转也不是不可能,但结果可能是程序结构七零八落,而且最终结果也不容易保证,总之,得不偿失。
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 51个

财富等级: 招财进宝

发表于 2013-5-9 00:01:58 | 显示全部楼层
ll_j 发表于 2013-5-8 23:54
我也是很早就使用递归了,但有些早期程序现在看起来夸张点说是有点后怕,以前有个程序的递归竟然是出错才 ...

可否大师把早期有过错误的代码贴来论坛大家分析分析。

点评

(defun ca_main() (princ (strcat "\n\n当前为" ca:row "列模式; 长度单位 " ca:c_m ";列间距 " (rtos ca:h_ 2 1) "; 文本字高 " (rtos ca:hh 2 2) "; 对齐方式 " ca:jm "。\n")) (initget 1 "U N C F H J") (se  详情 回复 发表于 2013-5-9 10:06
论坛插件加载方法
发帖求助前要善用【论坛搜索】功能,那里可能会有你要找的答案;
如果你在论坛求助问题,并且已经从坛友或者管理的回复中解决了问题,请把帖子标题加上【已解决】;
如何回报帮助你解决问题的坛友,一个好办法就是给对方加【D豆】,加分不会扣除自己的积分,做一个热心并受欢迎的人!
回复 支持 反对

使用道具 举报

已领礼包: 6530个

财富等级: 富甲天下

发表于 2013-5-9 10:06:26 | 显示全部楼层
本帖最后由 ll_j 于 2013-5-9 10:32 编辑
Lispboy 发表于 2013-5-9 00:01
可否大师把早期有过错误的代码贴来论坛大家分析分析。
下面这是前一版钢筋表计算程序ca.lsp的主程序,在进行选项设置时使用了“递归”,并在设置结束后就进行了后续计算并输出,最终在设置结束后进入计算,但在一层嵌套结束后,又进入上一层嵌套,结果又一次对“后续计算及输出”部分进行了重复,这是不允许的,但因为程序代码的问题,实际上到上一层计算时,部分变量变化(出错),最终并没有实际进行下去,所以输出结果还是正确的。
这个程序之所以最终结果是对的,应该很大程度上归功于一些判断功能的函数(if、while、cond等)的容错性,而且这个错误在我其它程序里还有。
可以看出,递归在程序进行选项设置时运用,不管是效率上,还是可读性上都是很有优势的,但使用不当也会有一些意想不到的问题,经过反复思考,发现问题就出在后续程序上,按本例,可以把设置部分单独拿出(作为子函数),进行递归,设置结束后,再根据设置结果进行后续计算。
因为当时没有想明白。所以在改写的ca.lsp中,设置部分改用了while循环,虽然结果是相同的,但那些判断条件可是有些“伤”人。
附件贴出了前一版ca的源程序,新版ca可以到我网络U盘下载。


[pcode=lisp,true](defun ca_main()
  (princ (strcat "\n\n当前为" ca:row "列模式; 长度单位 " ca:c_m ";列间距 " (rtos ca:h_ 2 1) "; 文本字高 " (rtos ca:hh 2 2) "; 对齐方式 " ca:jm "。\n"))
  (initget 1 "U N C F H J")
  (setq pt1 (getpoint (strcat "列数N/单位U/变更列间距C/自由列间距F/文本字高H/对齐方式J/<给出钢筋总长" ca:jm "对齐点>: ")))
  (cond
    ((= pt1 "U")
    (if (= ca:c_m "cm") (setq ca:c_m "mm" ca:cmm 0.001) (setq ca:c_m "cm" ca:cmm 0.01))
    (ca_main)               ;自身调用-递归
    )
    ((= pt1 "N")
    (if (= ca:r_w 3) (setq ca:r_w 2 ca:row "二") (setq ca:r_w 3 ca:row "三"))
    (ca_main)               ;自身调用-递归
    )
    .......
    (t
      (setq n6_ 0)
      (if (= ca:r_w 3)
        (setq ptx1 (car pt1)
                 ptx2 (+ ptx1 (* 0.95 ca:h_))
                 ptx3 (+ ptx1 (* 2.0 ca:h_))
        )
        (setq ptx1 (car pt1)
                 ptx3 (+ ptx1 (* 1.0 ca:h_))
        )
      )
    )
  )
  (while (> l1 0)           ;后续计算并输出
  (ca_smax se1 l1)
  (setq e01 e20 n1 (atof (ca_f (cdr (assoc 1 e01)))) se1 se0)
  ......
  )
)
[/pcode]

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

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-3-29 12:59 , Processed in 0.464309 second(s), 61 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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