tweak-text:Lisp嵌套超过`max-lisp-eval-depth

时间:2012-06-14 02:54:41

标签: emacs lisp elisp land-of-lisp

程序应重新格式化字符串,如下所示。

示例:(游戏打印'(这是一种句子。这是什么?可能。)

这是一句话。那怎么样?可能。

但是出了点问题(Lisp嵌套超过了'max-lisp-eval-depth),我不知道为什么。这段代码实际上来自第97页的“Land of lisp”一书。原始代码是用普通的lisp编写的。我想在elisp中重写它。 tweak-text中的最后两个参数意味着captain和literal。

(defun tweak-text (lst caps lit)
  (when lst
    (let ((item (car lst))
          (rest (cdr lst)))
      (cond ((eql item ?\ ) (cons item (tweak-text rest caps lit)))
            ((member item '(?\! ?\? ?\.)) (cons item (tweak-text rest t lit)))
            ((eql item ?\") (tweak-text rest caps (not lit)))
            (lit (cons item (tweak-text rest nil lit)))
            (caps (cons (upcase item) (tweak-text rest nil lit)))
            (t (cons (downcase item) (tweak-text rest nil nil)))))))

(defun game-print (lst)
  (print (coerce (tweak-text (coerce (prin1-to-string lst) 'list) t nil) 'string)))

(game-print '(not only does this sentence have a "comma," it also mentions the "iPad."))

用共同的lisp写的原始代码。

(defun tweak-text (lst caps lit)
  (when lst
    (let ((item (car lst))
          (rest (cdr lst)))
      (cond ((eql item #\space) (cons item (tweak-text rest caps lit)))
            ((member item '(#\! #\? #\.)) (cons item (tweak-text rest t lit)))
            ((eql item #\") (tweak-text rest caps (not lit)))
            (lit (cons item (tweak-text rest nil lit)))
            (caps (cons (char-upcase item) (tweak-text rest nil lit)))
            (t (cons (char-downcase item) (tweak-text rest nil nil)))))))

(defun game-print (lst)
    (princ (coerce (tweak-text (coerce (string-trim "() " (prin1-to-string lst)) 'list) t nil) 'string))
    (fresh-line))

5 个答案:

答案 0 :(得分:4)

在这两种情况下,您都有非终端递归,因此您正在使用 O(长度(lst))堆栈空间。显然,系统可能会限制堆栈 你可以使用的空间,你确实在emacs中达到了这个限制。 (现在 然后在emacs中,您可以通过更改来增加限制 max-lisp-eval-depth,但这不能解决根本问题。)

解决方案是使用迭代而不是递归。

但首先,写一下emacs:

(defun character (x)
  "common-lisp: return the character designated by X."
  (etypecase x
    (integer x)
    (string (aref x 0))
    (symbol (aref (symbol-name x) 0))))

(defun string-trim (character-bag string-designator)
  "common-lisp: returns a substring of string, with all characters in \
character-bag stripped off the beginning and end."
  (unless (sequencep character-bag)
    (signal 'type-error  "expected a sequence for `character-bag'."))
  (let* ((string (string* string-designator))
         (margin (format "[%s]*" (regexp-quote
                                  (if (stringp character-bag)
                                      character-bag
                                      (map 'string 'identity character-bag)))))
         (trimer (format "\\`%s\\(\\(.\\|\n\\)*?\\)%s\\'" margin margin)))
    (replace-regexp-in-string  trimer "\\1" string)))

(require 'cl)

这样你就可以为CL和elisp写一个函数:

(defun tweak-text (list caps lit)
  (let ((result '()))
    (dolist (item list (nreverse result))
      (cond ((find item " !?.")          (push item result))
            ((eql item (character "\"")) (setf lit (not lit)))
            (lit                         (push item result)
                                         (setf caps nil))
            (caps                        (push (char-upcase item) result)
                                         (setf caps nil))
            (t                           (push (char-downcase item) result)
                                         (setf caps nil
                                               lit nil))))))

(defun game-print (list)
  (princ (coerce (tweak-text (coerce (string-trim "() " (prin1-to-string list)) 'list)
                             t nil)
                 'string))
  (terpri))

然后:

(game-print '(not only does this sentence have a "comma," it also mentions the "iPad."))
在emacs中

prints:   Not only does this sentence have a comma, it also mentions the iPad.
returns:  t

在Common Lisp中:

prints:   Not only does this sentence have a comma, it also mentions the iPad.
returns:  nil

现在,一般来说使用列表处理字符串没什么意义,emacs lisp和Common Lisp都有强大的原语来直接处理序列和字符串。

答案 1 :(得分:3)

请注意,elisp(遗憾地)不会针对尾部递归进行优化,因此这是编写此函数的一种非常低效的方法。

答案 2 :(得分:1)

在tweak-text中递归时,您确实达到了'max-lisp-eval-depth'限制。我没有看到代码的方式有什么问题(我没有检查它是否按照你的意图去做)。

您可以配置/提高'max-lisp-eval-depth'限制。该变量的文档声明只要您确信不会耗尽堆栈空间,就可以引发该变量。在我的机器上保守地将限制设置为541。将其提升到600,上面的函数定义就可以处理你给出的输入。

答案 3 :(得分:1)

正如Pascal Bourguignon已经提到过的那样,使用字符串将它们强制转换为列表并返回将是一种更好的方法,下面是我的看法。请注意,它略有不同,因为文字字符串被验证用于标点符号,并且如果它们看起来像标点符号,否则会导致下一个字母大写,那么它也将是大写字母。我不确定这是一个缺点,这就是为什么我没有处理这种差异。

(defun tweak-text (source)
  (let ((i 0) (separator "") (cap t) current)
    (with-output-to-string
      (dolist (i source)
        (setq current
              (concat separator 
                      (etypecase i
                        (string i)
                        (symbol (downcase (symbol-name i)))))
              separator " ")
        (let (current-char)
          (dotimes (j (length current))
            (setq current-char (aref current j))
            (cond
             ((position current-char " \t\n\r"))
             (cap (setq cap nil
                        current-char (upcase current-char)))
             ((position current-char ".?!")
              (setq cap t)))
            (princ (char-to-string current-char))))))))

(tweak-text '(not only does this sentence have a "comma," it also mentions the "iPad."))
"Not only does this sentence have a comma, it also mentions the iPad."

答案 4 :(得分:0)

我认为你应该这样写:

(defun tweak-text-wrapper (&rest args)
  (let ((max-lisp-eval-depth 9001)) ; as much as you want
    (apply tweak-text args)))