【问题标题】:Functions inside a loop behaves differently循环内的函数行为不同
【发布时间】:2016-02-08 07:01:30
【问题描述】:

所以我有一个循环来重复我制作的关于 dota 的小文字游戏,但是当在循环中调用函数“play”时,它不会返回我的 cond 函数的结果,它只需要一个输入然后进入下一个循环。

;;;;learn the invoker combo's
(defparameter *invoker-combo* '((cold-snap  (3 0 0) 'QQQ);all of the possible invoker combo's
                                (ghost-walk (2 1 0) 'QQW)
                                (Ice-Wall (2 0 1) 'QQE)
                                (EMP (0 3 0) 'WWW)
                                (Tornado (1 2 0) 'QWW)
                                (Alacrity (0 2 1) 'WWE)
                                (Sun-Strike (0 0 3) 'EEE)
                                (Forge-Spirit (1 0 2) 'QEE)
                                (Chaos-Meteor (0 1 2) 'WEE)
                                (Deafening-Blast (1 1 1) 'QWE)))
(defun rand-combo (invoker-combo);returns a random combo
    (nth (random (length invoker-combo))invoker-combo))

(defun count-letters (input);converts the keyboard strokes into numbers to be compared as it doesn't matter what order they are in, just that there is the correct quantity of them e.g QQE could also be written QEQ.
    (append
        (list (count #\Q input)
              (count #\W input)
              (count #\E input))))

(defun try-for-combo (rand-combo);takes i-p and compares it with the value for the random combo
    (print(car rand-combo))
    (let* ((i-p (string-upcase(read-line)))
            (try (count-letters i-p)))
            (cond ((equal try (cadr rand-combo))'Good-job)
                  ((equal i-p "END")(list 'Thanks 'for 'playing))
                  (t (list i-p 'was 'wrong 'correct 'is (caddr(assoc (car rand-combo)*invoker-combo*)))))))

(defun play ()
    (try-for-combo (rand-combo *invoker-combo*)))

(defun loop-play (x)
    (loop for i from 0 to x
        :do (play)))

如果我调用函数“播放”,我会得到以下 o/p:

FORGE-SPIRIT asdf
("ASDF" WAS WRONG CORRECT IS 'QEE)

ALACRITY wwe
GOOD-JOB

但如果我调用函数“循环播放”,我会得到以下 o/p:

Break 3 [7]> (loop-play 2)    
SUN-STRIKE eee    
ALACRITY wwe
TORNADO qww
NIL

有人可以向我解释为什么会这样吗? 编辑:随意更改标题,我真的不知道该放什么。

【问题讨论】:

  • 您可能希望更好地缩进和格式化您的代码。
  • 如果您看到类似BREAK 3 [7]> 的提示,那么您的 Lisp 交互是在调试器中。将其从调试器中取出。使用命令help 获取有关可用命令的帮助。 quit 让 Lisp 回到顶层。
  • 好的,谢谢。您是否有任何关于如何正确格式化我的代码的链接,因为这是我正在阅读的书中的做法,我也发布了有关它的问题,每个人都对它充满敌意,所以我不打扰。
  • 嗯,我看了看我正在阅读的这本书,只有一些小东西在美学或实际方面几乎没有什么区别,还有一些空白。
  • @Floofk 考虑使用将 rand-combo 中的调用者组合变量设为可选并默认为 invoker-combo。还为 invoker-combo 使用 alist,删除第 3 列,因为它是多余的。可以使用函数将列表表示形式转换为“人类可读”的表示形式。

标签: lisp common-lisp read-eval-print-loop clisp


【解决方案1】:

代码的缩进和格式很差。请方便您和我们阅读代码。

(defun try-for-combo (rand-combo);takes i-p and compares it with the value for the random combo
    (print(car rand-combo))
    (let* ((i-p (string-upcase(read-line)))
            (try (count-letters i-p)))
            (cond ((equal try (cadr rand-combo))'Good-job)  ; wrong indent level
                  ((equal i-p "END")(list 'Thanks 'for 'playing))
                  (t (list i-p 'was 'wrong 'correct 'is (caddr(assoc (car rand-combo)*invoker-combo*)))))))
  • s 表达式之间缺少空格
  • 缩进级别错误
  • 代码结构不清楚
  • 不使用内置文档功能
  • 有些行太长了

更好:

(defun try-for-combo (rand-combo)
  "takes i-p and compares it with the value for the random combo" ; built in doc
  (print (car rand-combo))
  (let* ((i-p (string-upcase (read-line)))
         (try (count-letters i-p)))
    (cond ((equal try (cadr rand-combo))                          ; indentation
           'Good-job)
          ((equal i-p "END")
           (list 'Thanks 'for 'playing))
          (t
           (list i-p 'was 'wrong 'correct 'is                     ; several lines
                 (caddr (assoc (car rand-combo)
                               *invoker-combo*)))))))

我建议使用真正理解某些 Lisp 格式的编辑器。比如 GNU Emacs / SLIME、Clozure CL 的 Hemlock、LispWorks 的编辑器……

如果你不确定格式化,你也可以让 Lisp 来做。 Clisp 不擅长格式化,但 SBCL 或 CCL 之类的东西可以:

* (let ((*print-case* :downcase))
  (pprint '(defun try-for-combo (rand-combo)
 (print (car rand-combo))
                    (let* ((i-p (string-upcase (read-line)))
 (try (count-letters i-p)))
                            (cond ((equal try (cadr rand-combo))
 'Good-job) ((equal i-p "END")
                              (list 'Thanks 'for 'playing))
 (t (list i-p 'was 'wrong 'correct 'is
                                      (caddr (assoc (car rand-combo)
 *invoker-combo*)))))))))

你会得到格式很好的代码:

(defun try-for-combo (rand-combo)
  (print (car rand-combo))
  (let* ((i-p (string-upcase (read-line))) (try (count-letters i-p)))
    (cond ((equal try (cadr rand-combo)) 'good-job)
          ((equal i-p "END") (list 'thanks 'for 'playing))
          (t
           (list i-p 'was 'wrong 'correct 'is
                 (caddr (assoc (car rand-combo) *invoker-combo*)))))))

编辑器自动缩进 Lisp 代码可以为您节省大量工作。

manual indentation的提示。

【讨论】:

  • 感谢您的帮助 :) 我很感激。有什么小方法可以记住正确的缩进和格式吗?比如一句话什么的。我不使用 Emacs,因为当我刚开始时,必须记住如何使用 Emacs 和一些基本的 lisp 非常令人困惑,所以当我换书时我只使用 Notepad++,因为现在我可以专注于学习 LISP 而不是LISP 和 Emacs。
  • @Floofk:使用 Notepad++ 意味着您需要专注于手动缩进代码和其他事情。 Emacs 会为你做这件事,这极大地简化了 Lisp 编程。学习 Emacs(或任何其他支持 Lisp 的编辑器)并立即获得回报。
  • 你把它卖给我了哈哈,我会得到 emacs 和 slime :)
【解决方案2】:

您的try-for-combo 函数实际上并没有输出 任何东西。相反,它返回值。

在 REPL 中,如果您评估一个表单,例如 (+ 1 2),它总是会在最后打印该表单的评估结果(在本例中为 3)。但是,请考虑使用(+ 1 (print 2))print 函数实际上将参数输出到标准输出,然后返回值本身。所以这将显示(在 repl 上)

2
3

首先输出2,因为(print 2)本身打印2。然后,(+ 1 (print 2)) 形式的计算结果与 (+ 1 2)3 相同。

在您的情况下,您的 try-for-combo 函数应如下所示:

(defun try-for-combo (rand-combo)
  (print (car rand-combo))
  (let* ((i-p (string-upcase(read-line)))
         (try (count-letters i-p)))
    (print
     (cond
       ((equal try (cadr rand-combo)) 'Good-job)
       ((equal i-p "END") (list 'Thanks 'for 'playing))
       (t (list i-p 'was 'wrong 'correct 'is (caddr(assoc (car rand-combo) *invoker-combo*))))))
    nil))

这将打印cond 表单的结果,并返回“nil”。

【讨论】:

  • 总结使用 print :D
【解决方案3】:

这只是你的程序的输出和 Lisp 系统为每次评估所做的输出之间的区别:

print 打印一些东西(一个换行符,然后是它的参数)并返回一个值。该值由 REPL 打印。因此我们看到了两次输出:

[3]> (print "3")

"3" 
"3"

接下来,我们在progn 中多次调用printprogn 表单的值由 REPL 打印。前三个字符串由代码打印,最后一个字符串打印,因为 Lisp REPL 打印了值:

[4]> (progn (print "1") (print "2") (print "3"))

"1" 
"2" 
"3" 
"3"

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-08-18
    • 2022-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-09
    • 1970-01-01
    相关资源
    最近更新 更多