【问题标题】:tweak-text: Lisp nesting exceeds `max-lisp-eval-depth调整文本:Lisp 嵌套超过 `max-lisp-eval-depth
【发布时间】:2012-06-17 01:40:12
【问题描述】:

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

示例:(game-print '(THIS IS A SENTENCE. WHAT THIS?probly.))

这是一个句子。那个呢?大概吧。

但是出了点问题(Lisp 嵌套超过了 `max-lisp-eval-depth),我不知道为什么。这段代码其实是出自《Land of lisp》一书97页。原始代码是用common lisp写的。我想用elisp重写它。调整文本中的最后两个参数表示船长和文字。

(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."))

common 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))

【问题讨论】:

    标签: emacs lisp elisp land-of-lisp


    【解决方案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."
    

    【讨论】:

      【解决方案2】:

      在这两种情况下,您都有非终端递归,因此您正在使用 O(length(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 都有强大的原语来直接处理序列和字符串。

      【讨论】:

        【解决方案3】:

        我认为你应该这样写:

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

        【讨论】:

          【解决方案4】:

          请注意,elisp(遗憾的是)没有针对尾递归进行优化,因此事实证明这是编写此函数的一种非常低效的方式。

          【讨论】:

            【解决方案5】:

            在调整文本中递归时,您确实达到了 'max-lisp-eval-depth' 限制。我看不出代码的方式有什么问题(我没有检查它是否按照您的意图执行)。

            您可以配置/提高“max-lisp-eval-depth”限制。该变量的文档指出,只要您确信自己不会遇到堆栈空间不足的情况,就可以提高它。我的机器上的限制保守地设置为 541。将其提高到 600 可以使上面的函数定义处理您作为示例提供的输入。

            【讨论】:

            • 谢谢,我跟踪 Backtrace 缓冲区,发现当它处理“d.”)”最后 4 个字符时,它命中了 'max-lisp-eval -depth'。我机器上的 'max-lisp-eval-depth' 是 500。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-08-16
            • 1970-01-01
            相关资源
            最近更新 更多