代码之家  ›  专栏  ›  技术社区  ›  Brave Shine

Lisp&rest参数和递归调用

  •  2
  • Brave Shine  · 技术社区  · 7 年前

    我有以下常见的Lisp函数:

    (defun test(A &rest indexes)
      (if (null (first indexes))
          A
        (test (nth (+ 1 (first indexes)) A) (rest indexes))
      )
    )
    

    据我所知 &rest 参数在函数体中被视为列表,但由于

    (rest indexes) 还返回一个我一直使用嵌套列表作为参数的列表。

    例如 (test '("a" "b" "c" ("d" "e")) 3 1 6 7)

    将导致索引 ((1 6 7)) 在第二次通话时。

    有没有办法通过我的名单而不出现这个问题?

    2 回复  |  直到 7 年前
        1
  •  3
  •   Rainer Joswig Michael Fox    7 年前

    基本样式规则:不使用 &rest 列表处理函数的参数。

    为什么?通用Lisp实现只允许支持以下值 CALL-ARGUMENTS-LIMIT 参数数。此数字为50或更大,具体取决于实施情况。

    这意味着您的函数在某些实现过程中可能会列出不超过50项的列表。

    较好的 :将列表作为单独的参数传递。

    (defun test (A indexes)
       ...)
    
    (test '("a" "b" "c" ("d" "e")) '(3 1 6 7))
    

    错误的解决方案 :不使用 apply ,因为它不能解决有限参数列表的问题。

    CLISP
    
    [1]> call-arguments-limit
    4096
    [2]> (defun l1 (&rest l) l)
    L1
    [3]> (apply #'l1 (loop repeat 5000 collect 1))
    
    *** - APPLY: too many arguments given to
          #<FUNCTION L1 (&REST L)
             (DECLARE (SYSTEM::IN-DEFUN L1))
             (BLOCK L1 L)>
    The following restarts are available:
    ABORT          :R1      Abort main loop
    
        2
  •  2
  •   Sylwester    7 年前

    rest 是与配对的访问器函数 first 为您提供列表的第一个元素和其余元素。 休息 与相同 cdr .

    &rest 是一个 lambda list keyword 这会在其后的变量名中删除其余参数。

    你真的在找 apply . 假设我创建了一个可以接受0个或更多数值参数并将其相加的函数:

    (defun add (&rest numbers)
      (apply #'+ numbers))
    

    Apply可以接受两个以上的参数。第一个是要调用的函数,除了最后一个之外,其余都是放在last arguments元素前面的额外参数。您可以保证该实现支持50个参数,或者最多支持一个函数在该特定实现中可以接受的50个以上的参数。

    (apply #'+ 1 2 '(3 4 5)) ; ==> 15
    

    现在通过递归 &休息 申请 使代码效率低下,因此应使用高阶函数、循环宏或生成助手:

    ;; higher order function
    (defun fetch-indexes (a &rest indexes)
      (mapcar (lambda (i) (nth i a)) indexes))
    
    ;; loop macro
    (defun fetch-indexes (a &rest indexes)
      (loop :for i :in indexes
            :collect (nth i a)))
    
    ;; helper function
    (defun fetch-indexes (a &rest indexes)
      (labels ((helper (indexes)
                 (if (endp indexes)
                     '()
                     (cons (nth (first indexes) a)
                           (helper (rest indexes))))))
        (helper indexes)))
    
    ;; test (works the same with all)
    (fetch-indexes '(a b c d) 2 3 0 1)
    ; ==> (c d a b)
    

    应该避免在递归中使用apply,但我将展示它是如何完成的。

    (defun fetch-indexes (a &rest indexes)
      (if (endp indexes)
          '()
          (cons (nth (first indexes) a)
                (apply #'fetch-indexes a (rest indexes)))))
    

    在您的示例中,有嵌套列表。为了让它起作用,你还需要把它展平。我还没有这样做,所以这些支持一个适当的元素列表。