【问题标题】:(define (average ....)) in Lisp(定义(平均 ....))在 Lisp 中
【发布时间】:2011-03-15 15:44:46
【问题描述】:

我只是在玩scheme/lisp,正在考虑如何纠正我自己对average 的定义。我不确定如何做一些我认为需要的事情。

  • 定义一个接受任意数量参数的过程
  • 计算这些参数
  • 将参数列表传递给 (+) 以将它们加在一起

有人有定义average 的例子吗?我似乎对 LISP 了解得不够多,无法形成一个网络搜索来返回我正在寻找的结果。

【问题讨论】:

  • 这不是家庭作业(由于某种原因被否决了......可能是因为有人认为它是)。我正在学习 SICP 在线课程,我只是在玩 LISP,想知道如何创建这个程序时间。
  • (FWIW,这很清楚 - 几乎所有课程都避免使用带有休息参数的函数。)

标签: lisp scheme racket


【解决方案1】:

定义将是一个非常简单的单行,但不要破坏它,您应该研究一下:

  • 一个“rest”参数——这个(define (foo . xs) ...xs...)foo定义为一个接受任意数量参数的函数,它们可以作为xs值的列表使用。

  • length 返回列表的长度。

  • apply 接受一个函数和一个值列表并将该函数应用于这些值。

当你得到它时,你可以去更多:

  • 请参阅foldl 函数以避免在可能非常大的列表上应用列表(这在参数列表长度有限的某些实现中可能很重要,但在 Racket 中不会有太大区别)。

  • 请注意,Racket 具有精确的有理数,您可以使用exact->inexact 来制作更高效的浮点版本。





剧透如下:

  • (define (average . ns) (/ (apply + ns) (length ns)))

  • 让它需要一个参数:(define (average n . ns) (/ (apply + n ns) (add1 (length ns))))

  • 使用foldl:(define (average n . ns) (/ (foldl + 0 (cons n ns)) (add1 (length ns))))

  • 使其使用浮点:(define (average n . ns) (/ (foldl + 0.0 (cons n ns)) (add1 (length ns))))

【讨论】:

    【解决方案2】:

    在 Common Lisp 中,看起来你可以这样做:

    (defun 平均 (&rest args) (当参数 (/ (apply #'+ args) (length args))))

    虽然我不知道 &rest 是否适用于 Lisp 的所有实现。参考here

    将该代码放入 GNU CLISP 会导致:

    [1]> (defun 平均 (&rest args) (当参数 (/ (apply #'+ args) (length args)))) 平均的 [2]>(平均 1 2 3 4 5 6) 7/2

    这是 3.5(正确)。

    【讨论】:

    • 你不应该假设 lisp 实现可以接受任意数量的输入(并且 IIRC、CLisp 会中断)。此外,该函数应始终返回一个数字——当没有输入时,此代码将返回 NIL。
    • @Eli,您可能比我更了解 LISP(正如您更全面的答案所证明的那样)。但我不得不不同意最后的说法。在数学上,空集的平均值不是一个数字,它是未定义的。如果您确实希望它返回 0 {ugh! IMNSHO :-) },你可以修改代码来做到这一点。
    • paxdiablo:对,它未定义的,在大多数情况下,最好通过抛出错误来将其转换为代码。例如看我的代码:当没有输入时它会抛出一个错误(在第一个版本中被零除错,稍后会出现更好的arity错误)。当然,在某些情况下,您希望返回一些“未定义”结果(nullundefinednan 等),但通常最好让代码抛出更早地出错,而不是让它传播潜在的虚假结果。
    • (哦,顺便说一句,任何 lisper 都会对“LISP”抱怨——应该是“Lisp”。)
    • 对不起,我以为是首字母缩略词:很多令人讨厌的多余括号 :-) 我只玩过一点 Lisp,更是如此,因为它是我们曾经采用的产品的一部分,但是现在我能记住的只是我在 vi 中必须做的无休止的 % 括号匹配。
    【解决方案3】:

    Common Lisp 中的两个版本:

    (defun average (items)
      (destructuring-bind (l . s)
          (reduce (lambda (c a)
                    (incf (car c))
                    (incf (cdr c) a)
                    c)
                  items
                  :initial-value (cons 0 0))
        (/ s l)))
    
    (defun average (items &aux (s 0) (l 0))
      (dolist (i items (/ s l))
        (incf s i)
        (incf l)))
    

    【讨论】:

      【解决方案4】:

      在Scheme中,我更喜欢使用列表而不是“rest”参数,因为rest参数使实现如下过程变得困难:

      > (define (call-average . ns)
           (average ns))
      > (call-average 1 2 3) ;; => BANG!
      

      将任意数量的参数打包到一个列表中允许您对参数执行任何列表操作。您可以用更少的语法和混乱做更多的事情。这是我的 average 的 Scheme 版本,它带有“n”个参数:

      (define (average the-list)
        (let loop ((count 0) (sum 0) (args the-list))
          (if (not (null? args))
              (loop (add1 count) (+ sum (car args)) (cdr args))
              (/ sum count))))
      

      这是 Common Lisp 中的相同过程:

      (defun average (the-list)
        (let ((count 0) (sum 0))
          (dolist (n the-list)
            (incf count)
            (incf sum n))
          (/ sum count)))
      

      【讨论】:

      • 您可以使用apply 将列表转换为参数列表。
      • (Incf sum n) 优于 (setf sum (+ sum n))。此外,我的第一个原型通常是(/ (reduce #'+ list) (length list)),然后我可能会将其更改为(loop for element in list counting element into length summing element into sum finally (return (/ sum length)))
      • 难道你不能写一个简单的宏来解压缩列表作为调用average的单独参数,所以它看起来像:(define (call-average . ns) (average (unpack ns)))?我目前是一个 Scheme/Lisp 新手,所以我真的不知道这怎么可能。
      【解决方案5】:

      在方案 R5RS 中:

      (define (average . numbers)  
          (/ (apply + numbers) (length numbers)))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-11-30
        • 2022-08-10
        • 1970-01-01
        • 2012-07-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多