【问题标题】:LISP- Improve recursion to tail recursion in rotate list functionLISP-在旋转列表函数中改进对尾递归的递归
【发布时间】:2012-06-15 18:33:10
【问题描述】:

我在 LISP 中有一个递归函数可以将列表向右或向左旋转,如下所示:

(如果是正数-往左推,如果是负数-往右推)

> (rotate-n ‘(5 6 7 8 9) -3)
    (7 8 9 5 6)

> (rotate-n ‘(5 6 7 8 9) 3)
    (8 9 5 6 7) 

功能:

(defun rotate-n (L n)
  (cond ((= n 0) L)
        ((> n 0) (rotate-n (rotate-left L) (- n 1)))
        ((< n 0) (rotate-n (rotate-right L) (+ n 1)))))

有没有办法使用尾递归获得相同的结果? rotate-right 和 rotate-left 功能正常。

编辑: 我很困惑。我上面写的函数是尾递归。如果我没记错的话,这个函数就是为了相同目的的常规递归:

(defun rotate-n-r (L n)
  (cond ((= n 0) L)
        ((> n 0) (rotate-left (rotate-n-r L (- n 1))))
        ((< n 0) (rotate-right (rotate-n-r L (+ n 1))))))

【问题讨论】:

  • 您的左旋转和右旋转功能可用吗?因为它们必须被修改/包含在 rotate-n 中才能使尾递归起作用。
  • 当然,我写了它们并且它们有效。你认为有可能用尾递归解决它吗?
  • 是的,但我不确定这是一个好主意:无论哪种方式,效果都完全相同。这种方法几乎是尾递归:在进行递归调用之前旋转。
  • 为什么你认为当前的实现不是尾递归的? cond 的最后一次调用是 rotate-n
  • @paul 在这里拥有它的权利。除非你的意思是你希望你的 rotate-left 和 -right 函数是尾递归的,这可能是也可能不是。

标签: recursion functional-programming lisp rotation tail-recursion


【解决方案1】:

这是一个尾递归函数,可以满足您的需求:

(defun rotate-list (list n)
  (cond ((plusp n)
         (rotate-list
          (append (rest list) (list (first list)))
          (1- n)))
        ((minusp n)
         (rotate-list
          (append (last list) (butlast list))
          (1+ n)))
        (t list)))

请注意,它消耗(即分配内存)很多:

(ext:time (rotate-list '(5 6 7 8 9) 30))
                                           Permanent            Temporary
Class                                 instances   bytes    instances   bytes
-----                                 --------- ---------  --------- ---------
CONS                                          5        80        145      2320
-----                                 --------- ---------  --------- ---------
Total                                         5        80        145      2320
Real time: 3.2E-5 sec.
Run time: 0.0 sec.
Space: 2400 Bytes
(5 6 7 8 9)

一个非consing版本:

(defun nrotate-list (list n )
  (cond ((plusp n)
         (nrotate-list
          (nconc (rest list) (progn (setf (cdr list) nil) list))
          (1- n)))
        ((minusp n)
         (nrotate-list
          (nconc (last list) (nbutlast list))
          (1+ n)))
        (t list)))

在尾递归时不分配任何东西:

(time (nrotate-list '(5 6 7 8 9) 30))
Real time: 2.3E-5 sec.
Run time: 0.0 sec.
Space: 0 Bytes
(5 6 7 8 9)

请注意,这两个版本在性能方面都非常愚蠢 (他们为 each 迭代扫描列表两次,即, 它们的时间复杂度是O(n*length(list)))。

高效的版本将只扫描列表一次(即时间复杂度O(length(list)))并且不会是递归的。

【讨论】:

    猜你喜欢
    • 2019-09-14
    • 1970-01-01
    • 1970-01-01
    • 2016-01-06
    • 2016-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多