【问题标题】:LISP iterative to recursiveLISP 迭代到递归
【发布时间】:2018-06-19 15:33:37
【问题描述】:

我使用循环函数在 LISP 中写下了这个迭代代码:

(defun loadfile (filename)
  (with-open-file (stream filename)
    (loop for line = (read-line stream nil 'eof)
          until (eq line 'eof)
          collect line)))
    )
  )

有没有办法在没有loop的情况下以递归方式重写它?

【问题讨论】:

  • 是的,但它看起来像一个需要递归方法的任务吗?
  • 是的,我确实使用了循环,但任务(不幸的是)清楚地说 “不要使用 LOOP、DO、DOLIST、DOTIMES”
  • 我明白了;请注意,tagbody 显然没有任何限制;-)
  • 啊好吧,这是作业吧?要在现实生活中分两行执行此操作,我建议:stackoverflow.com/questions/3813895/…

标签: recursion iteration lisp common-lisp


【解决方案1】:

用 GOTO 来给他们惊喜:

(defun loadfile (filename)
  (with-open-file (stream filename)
    (prog (line lines)
      repeat
      (setf line (read-line stream nil))
      (when line
        (push line lines)
        (go repeat))
      (return (reverse lines)))))

【讨论】:

    【解决方案2】:

    当然,任何循环都可以递归转换,但是一次读取整个文件一行是一个典型的迭代过程,所以我觉得这个问题很难激发。

    这是一个可能的递归版本,其中递归由内部函数管理:

    (defun load-file (filename)
      (with-open-file (stream filename)
        (labels ((read-recursively ()
                   (let ((line (read-line stream nil 'eof)))
                     (if (eq line 'eof)
                         nil
                         (cons line (read-recursively))))))
          (read-recursively))))
    

    如果文件的行数很大,这个解决方案很容易出现堆栈溢出错误。

    如果有一个可以执行尾部优化的编译器,则以下替代递归解决方案可以以迭代方式编译并且可以避免堆栈溢出:

    (defun load-file (filename)
      (with-open-file (stream filename)
        (labels ((read-recursively (read-so-far)
                   (let ((line (read-line stream nil 'eof)))
                     (if (eq line 'eof)
                         (reverse read-so-far)
                         (read-recursively (cons line read-so-far))))))
          (read-recursively ()))))
    

    【讨论】:

    • 文件必须只包含一行(可能很长),所以我认为这两种解决方案中的每一种都可以使用,对吗?
    • @DouglasP 只有一行?那为什么要循环,只需调用一次read-line
    • 是的,但我想尝试一下,以防我有更多行。谢谢
    猜你喜欢
    • 2012-01-23
    • 1970-01-01
    • 2016-01-05
    • 2013-12-31
    • 1970-01-01
    • 1970-01-01
    • 2020-11-14
    相关资源
    最近更新 更多