【问题标题】:Lisp basic print function getting user inputLisp 基本打印功能获取用户输入
【发布时间】:2021-02-24 12:36:12
【问题描述】:

我应该编写一个程序,将简单的用户输入作为字符串获取,并且代码应该写回响应(姓名,你是不是一个人等)。该程序假设在输入单词“再见”时终止。代码如下:

(defun x()
    (setq words "empty")
    (loop while (string/= words "BYE")
        (setq words (read-delimited-list #\~)   
        (write-line words)
        (format t "ROBBIE%: Hello, who are you?")
        (case (string-include "I'm" words)
            (format t "ROBBIE%: Nice to see you, how are you?")
            ((string-include "Hi" words)
            (format t "ROBBIE%: How are you?")
            (or (string-include "fine" words) (string-include "person" words))
            (format t "ROBBIE%: No I'm a computer")))
            (format t "BYE"))

(x)

但是,当我在程序 2 上编译此错误时:

Line2:3 警告:未定义变量:COMMON-LISP-USER::WORDS

Line3:3 错误:在 (LOOP WHILE (STRING/= WORDS "BYE") ...) 的宏展开期间。使用BREAK-ON-SIGNALS进行拦截。

我已经在 python 中完成了编程,但这对我来说是非常复杂的语言,我需要一些帮助来理解为什么这不起作用?非常感谢任何建议!

【问题讨论】:

  • 上面的代码有不平衡的括号,并且没有正确缩进。你能先解决这些问题吗?
  • 对此我深表歉意。现在应该正确缩进。括号非常混乱,还在学习中。
  • 括号可以(并且应该)在编辑器的帮助下进行跟踪,例如github.com/susam/emacs4cl 的详细解释;在开箱即用的 Emacs 中突出显示匹配的括号,这已经比在文本编辑器(记事本等)中编写更好。
  • 另外我鼓励你先编写小的表达式或函数,在 REPL 中测试它们,只有当你对程序中发生的事情感到满意时,才能构建更大的函数。否则,您可能需要捕获多个错误(如此处)。

标签: lisp common-lisp


【解决方案1】:

当你这样做时:

(defun x ()
  (setf words "foo"))

那么words 没有定义。它引用了一些全局变量,如果它不存在,它将创建它,但可能在范围和范围方面有一些奇怪的行为。这不是可移植的代码。

为了引入局部变量,使用let

(defun x ()
  (let ((words "foo"))
    ;; words is is scope here
    )
  ;; but not here
  )

Loop(以更常见的»扩展«形式)在其所有子句中使用循环关键字。没有隐含的身体。为了做某事,您可以使用do,它允许遵循多个表单:

(defun x ()
  (let ((words "foo"))
    (loop while (string/= words "bye")
          do (setf words (read-line …))
             (format …))))

Case 使用编译时值与eql 进行比较:

(case something
  (:foo (do-a-foo))
  ((:bar :baz) (do-a-bell))
  (t (moooh)))

这不适用于字符串,因为字符串不是eql,除非它们是相同的object(即它们是eq)。在你的情况下,你想要一个cond:

(cond ((string-include-p words "Whatever")
       …)
      ((string-include-p words "yo man")
       …))

对于与用户的交互,您可能希望使用双向 *query-io* 流:

(format *query-io* "Who am I?")

(read-line *query-io*)

Read-line 为您提供字符串,并且似乎比 read-delimited-list 更适合您的任务,read-delimited-list 有其他用例。

【讨论】:

    【解决方案2】:

    让我专注于其他解决方案尚未涵盖的代码方面。

    循环

    这是你的循环结构:

    (let ((words "empty"))
      (loop
        while (string/= words "BYE")
        do
        (progn
          (setq words (read-line)))))
    

    首先,在do 之后你不需要(progn ...)。你也可以这样写:

    (let ((words "empty"))
      (loop
        while (string/= words "BYE")
        do (setq words (read-line))))
    

    必须将words 初始化为某个任意值(有时称为哨兵值)是一种代码异味(并不总是坏事,但可能有更好的选择)。在这里,您可以使用for 子句来简化循环。

    (loop
      for words = (read-line)
      while (string/= words "BYE")
      do ...)
    

    此外,您可能希望将untilstring= 测试一起使用,这可能更具可读性:

    (loop
      for words = (read-line)
      until (string= words "BYE")
      do ...)
    

    搜索

    您可以使用SEARCH 测试字符串是否包含。下面是一段带注释的 sn-p 代码,用于展示字符串操作函数是如何工作的:

    (defun test-I-am (input)
      (let ((start (search "I'am" input)))
        (when start
          ;; we found an occurrence at position start
          ;; let's find the next space character
          (let ((space (position #\space input :start start)))
            (when space
              ;; we found a space character, the name starts just after
              (format nil "Hello ~a!" (subseq input (1+ space))))))))
    

    使用这个简单的算法,这里有一个测试(例如在你的 REPL 中):

    > (test-i-am "I'am tired")
    "Hello tired!"
    

    【讨论】:

      【解决方案3】:

      read-line 替换read-delimited-list,用cond 替换大小写并平衡一些括号。这是可行的解决方案,包括一些用于字符串包含的功能:

      (defun string-include (string1 string2)
        (let* ((string1 (string string1)) (length1 (length string1)))
          (if (zerop length1)
              nil 
              (labels ((sub (s)
                         (cond
                          ((> length1 (length s)) nil)
                          ((string= string1 s :end2 (length string1)) string1)
                          (t (sub (subseq s 1))))))
                (sub (string string2))))))
      
      (defun x ()
          (let ((words "empty"))
            (format t "ROBBIE%: Hello, who are you?~%")
            (loop while (string/= words "BYE") do
                  (progn
                    (finish-output)
                    (setq words (read-line))   
                    (cond ((string-include "I'm" words)
                           (format t "ROBBIE%: Nice to see you, how are you?~%"))
                          ((string-include "Hi" words)
                           (format t "ROBBIE%: How are you?~%"))
                          ((or (string-include "fine" words) 
                               (string-include "person" words))
                           (format t "ROBBIE%: No I'm a computer~%")))))
                  (format t "BYE")))
      

      那你就叫它吧:

      (x)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-01-27
        • 1970-01-01
        • 2016-06-15
        • 1970-01-01
        • 2013-08-27
        • 2020-10-26
        • 2023-02-24
        • 1970-01-01
        相关资源
        最近更新 更多