【问题标题】:Lisp - Keep words finishing by given letterLisp - 保持单词以给定字母结尾
【发布时间】:2020-11-08 16:54:07
【问题描述】:

我试图修改这个函数,当给定一个列表时,它只会保留以给定字母结尾的单词。我对允许使用的内容几乎没有限制,并且需要保留 char、rplacd 和长度才能做到这一点。我现在在“长度”部分遇到困难。我最初设法做到这一点,它会保留所有以给定字母开头的单词,但我在第 5 行做相反的事情时遇到了麻烦。

(setq liste '(have read nose art silence)) 我会得到以下结果 (endingwith 'e liste) => (have nose silence)

(defun endingwith (x liste)
   (cond
      ((not liste) nil)
      ((equal
            (char (string (length (car liste))) 0) 
            (char (string x) 0) )
         (rplacd liste (endingwith x (cdr liste))) )
      (t (endingwith x (cdr liste))) ) )

【问题讨论】:

    标签: lisp common-lisp clisp


    【解决方案1】:

    请注意,您被分配的任务教授了一种在现实世界中未被使用的 Lisp 编程风格。

    • 我们需要对字符串进行操作,字符串是字符的向量
    • 我们可以使用标准函数remove
    • 破坏性地更改列表有时很有用,但可以避免。请参阅 delete 以获取 remove 的破坏性版本

    例子:

    (defun keep-symbols-ending-with-char (char symbols)
      "returns a sequence, where all symbols end with the given char"
      (when (symbolp char)
        (setf char (char (symbol-name char) 0)))
      (remove char
              symbols
              :test-not #'eql
              :key (lambda (item &aux (string (symbol-name item)))
                     (char string (1- (length string))))))
    
    CL-USER> (keep-symbols-ending-with-char 'e '(have read nose art silence))
    (HAVE NOSE SILENCE)
    

    【讨论】:

      【解决方案2】:

      鉴于您获得的资源有限,这需要递归解决方案。 (endingwith 'e liste) 的值应该根据使用列表的其余部分调用 endwith 的值来定义,如果它匹配 'e,则添加或不添加第一个元素。
      进一步注意,在您的情况下,长度应与字符串一起使用,因此请使用 (length (string (car liste))) 而不是 (string (length (car liste)))。
      该函数如下所示:

      (defun endingwith (x liste)
         (cond
            ((not liste) nil)
            ((eql (char (string x) 0) (char (string (car liste)) (- (length (string (car liste))) 1)))
                (cons (car liste) (endingwith x (cdr liste))) )
            (t (endingwith x (cdr liste))) ))
      

      【讨论】:

        【解决方案3】:

        一些风格要点:不要使用(not liste);而是使用(null liste)(endp liste),它们分别强调liste 要么是一个空列表,要么处理已经到达liste 的末尾。此外,当打算表示一个空列表时,请使用'();当意图表示布尔值 False 时使用 nil

        liste的元素是符号,x本身就是一个符号;需要将这些符号转换为序列,以便评估符号的最终字符。 string 将完成这项工作。但是这里的OP代码有两个问题:length带有一个序列参数,所以(car liste)的值也必须用string转换;在 Common Lisp 中,序列是零索引的,所以序列的最后一个索引比它的长度小一。

        (defun endingwith (x liste)
          (cond
            ((null liste) '())
            ((equal (char (string (car liste))
                          (- (length (string (car liste))) 1))
                    (char (string x) 0))
             (rplacd liste (endingwith x (cdr liste))))
            (t
             (endingwith x (cdr liste)))))
        

        在 Common Lisp 中调试此类程序的一种方法是进入 REPL 并进行实验。当您使用一个函数并将您发送到调试器时,请在该函数中查找可能有问题的行。

        在发布的代码中,(char (string (length (car liste))) 0) 是第一个可能的候选者。在 REPL 中尝试(car liste),看看它是否按预期计算为'HAVE。如果出现这种情况,请尝试(length (car liste))。这会将您再次发送到调试器,并显示类型错误和类似

        的消息

        LENGTH: HAVE is not a SEQUENCE.

        这表明您需要像在原始函数定义的下一行使用(string x)一样使用(string (car liste))。所以,在 REPL 上试试(length (string (car liste)))。现在您应该看到预期值 4,但很明显原始代码行有点混乱,因为char 希望第一个参数是字符串,而第二个参数是索引。所以在 REPL (char (string (car liste)) (length (string (car liste)))) 再试一次。这再次使我们进入调试器并显示如下消息:

        CHAR: index 4 should be less than the length of the string.

        但该消息提醒我们,在 Common Lisp 中序列是零索引的,长度为 4 的字符串的最后一个索引是 3。所以,再一次在 REPL:(char (string (car liste)) (- (length (string (car liste))) 1))。现在我们成功了,REPL 返回了预期的#\E。在 REPL 解决了这个有问题的行之后,我们现在可以替换原始函数定义中的行,看看它是否有效。确实如此。

        【讨论】:

          【解决方案4】:
          (defun ends-with-p (end s)
            (string= end (subseq s (- (length s) (length end)))))
          
          (defun keep-ending-with (end strings)
            (remove-if-not #'(lambda (x) (ends-with-p end x)) strings))
          

          【讨论】:

            猜你喜欢
            • 2022-12-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-12-19
            • 2017-07-09
            • 1970-01-01
            • 2019-03-02
            相关资源
            最近更新 更多