基本的LISP递归替换子字符串

时间:2019-11-27 22:19:30

标签: replace lisp common-lisp

我正在尝试使用递归函数中的基本LISP命令(例如,否mapcar)将列表中出现的给定字符串替换为“ X”。

(defun removetext (symbol list replaced)
    (if (not list)   ; if initial list has been exhausted, return replaced list
        replaced
        (progn
            (if (eql (car list) symbol)   ; otherwise, if first element of list = symbol, replace with "X"
                (removetext symbol (cdr list) (cons "X" replaced)) 
                (removetext symbol (cdr list) (cons (car list) replaced)) ; otherwise keep it 
            )
            (format t "~D" replaced)
        )
    )
)

如果我用(removetext "E" '(A B C D E F F E) "")调用它,则不起作用。

这将返回NIL,并且打印输出看起来像(F F E D C B A . )(F E D C B A . )(E D C B A . )(D C B A . )(C B A . )(B A . )(A . )

我希望它返回(A B C D X F F X)

3 个答案:

答案 0 :(得分:2)

(defun removetext (symbol list replaced)
  (if (null list)
      (reverse replaced)
    (if (eql (car list) symbol)
        (removetext symbol (cdr list) (cons 'x         replaced)) 
        (removetext symbol (cdr list) (cons (car list) replaced)))))

示例:

CL-USER > (removetext 'E '(A B C D E F F E) ())
(A B C D X F F X)

答案 1 :(得分:2)

到目前为止,没有其他答案可以解决代码中的重要问题。鉴于此函数的版本略有不同,但结构类似:

(defun replace-thing (list thing by &key (test #'eql))
  (labels ((replace-it (tail replaced)
             (if (null tail)
                 (reverse replaced)
               (progn
                 (if (funcall test (first tail) thing)
                     (replace-it (rest tail) (cons by replaced)) 
                   (replace-it (rest tail) (cons (first tail) replaced)))
                 (format t "~&got ~S~%" replaced)))))
    (replace-it list '())))

这将以与您的方法之一相同的方式失败:

 > (replace-thing '(a b c) 'a 'c)
 (b c)
 (c)
 nil
nil

失败的原因是因为progn所做的事情。你写了

(progn
  ... compute the thing you care about ...
  (format ...))

progn的值是其主体中 last 形式的值,因此在这种情况下,(format ...)的值就是nil

对此有两种解决方案:一种对您有用,另一种更为笼统。

对您有用的解决方案是认识到replaced永远不会改变(您的函数不会发生突变,这很好),因此您可以先 安全地打印它:

(progn
  (format t "~& ~S~%" replaced)
  (if (funcall test (first tail) thing)
      (replace-it (rest tail) (cons by replaced)) 
    (replace-it (rest tail) (cons (first tail) replaced))))

此更改将使我的功能正常工作。

一个更通用的解决方案是意识到您想要的是某种形式,例如first-thing,当用作

(first-thing
  thing1
  ...
  thingn)

将:

  1. 按顺序在体内做事情;
  2. 然后返回它们的 first 的值。

好吧,您可以很容易地将这种形式编写为宏。这是一个受限制的版本:

(defmacro first-thing (thing1 &body more-things)
  (let ((sn (make-symbol "STASH")))
    `(let ((,sn ,thing1))
       ,@more-things
       ,sn)))

然后

> (first-thing 1 (print "hi") (print "there"))

"hi" 
"there" 
1

但是实际上您不需要这样做:CL提供了受限制的一个和更通用的一个:

  • 受限制的是prog1(prog1 thing1 ... thingn)依次评估事物,然后返回第一件事的第一个值;
  • 一般是multiple-value-prog1(multiple-value-prog1 thing1 ... thingn)按顺序评估事物,然后返回 all 第一个事物的值。

multiple-value-prog1几乎肯定比prog1昂贵,因为它需要将多个值存储在某个地方,因此几乎可以肯定是存在问题的。就您而言,尽管您只需要prog1,所以您的函数的一个版本(我的版本)是:

(defun replace-thing (list thing by &key (test #'eql))
  (labels ((replace-it (tail replaced)
             (if (null tail)
                 (reverse replaced)
               (prog1
                 (if (funcall test (first tail) thing)
                     (replace-it (rest tail) (cons by replaced)) 
                   (replace-it (rest tail) (cons (first tail) replaced)))
                 (format t "~& ~S~%" replaced)))))
    (replace-it list '())))

> (replace-thing '(a b c) 'a 'z)
 (b z)
 (z)
 nil
(z b c)

答案 2 :(得分:1)

如果要替换出现的字符串,则需要传递字符串列表。您传递的是符号列表,而不是字符串。现在,您仍然可以将这些符号替换为特定的字符串,但是它们本身并不是字符串。列表:

'(a b c d e f e)

是符号列表,而不是字符串:

 (defun remove-text (str lst)
   (if (atom lst)
       lst
       (if (string-equal (symbol-name (car lst)) str)
           (cons "X" (remove-text str (cdr lst)))
           (cons (car lst) (remove-text str (cdr lst))))))

我们可以传递一个字符串作为参数进行检查,然后传递一个符号列表。我们的基本情况将测试以查看列表是否为原子,这时我们将简单地返回列表。如果不是,我们将使用string-equal来查看列表中汽车的符号名称(它将返回符号的字符串值)是否等于我们的字符串。然后,在这种情况下,我们可以将“ X”限制在列表的开头,然后再次递归调用该函数。如果不是,我们可以简单地将列表的汽车限制在列表的开头,然后再次递归调用该函数。该函数不是尾递归的,而是递归的,它确实避免了创建新的列表结构来存储您的结果,而不会破坏性地更改原始列表的结构。我们可以在下面对其进行测试:

CL-USER> (remove-text "E" '(a b c d e f f e))
(A B C D "X" F F "X")
相关问题