-->

(define (average …)) in Lisp

2020-07-09 08:57发布

问题:

I'm just playing around with scheme/lisp and was thinking about how I would right my own definition of average. I'm not sure how to do some things that I think are required though.

  • define a procedure that takes an arbitrary number of arguments
  • count those arguments
  • pass the argument list to (+) to sum them together

Does someone have an example of defining average? I don't seem to know enough about LISP to form a web search that gets back the results I'm looking for.

回答1:

The definition would be a very simple one-liner, but without spoiling it, you should look into:

  • a "rest" argument -- this (define (foo . xs) ...xs...) defines foo as a function that takes any number of arguments and they're available as a list which will be the value of xs.

  • length returns the length of a list.

  • apply takes a function and a list of values and applies the function to these values.

When you get that, you can go for more:

  • see the foldl function to avoid applying a list on a potentially very big list (this can matter in some implementations where the length of the argument list is limited, but it wouldn't make much difference in Racket).

  • note that Racket has exact rationals, and you can use exact->inexact to make a more efficient floating-point version.





And the spoilers are:

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

  • Make it require one argument: (define (average n . ns) (/ (apply + n ns) (add1 (length ns))))

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

  • Make it use floating point: (define (average n . ns) (/ (foldl + 0.0 (cons n ns)) (add1 (length ns))))



回答2:

In Common Lisp, it looks like you can do:

(defun average (&rest args)
  (when args
    (/ (apply #'+ args) (length args))))

although I have no idea if &rest is available on all implementations of Lisp. Reference here.

Putting that code into GNU CLISP results in:

[1]> (defun average (&rest args)
       (when args
         (/ (apply #'+ args) (length args))))
AVERAGE
[2]> (average 1 2 3 4 5 6)
7/2

which is 3.5 (correct).



回答3:

Two versions in 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:

In Scheme, I prefer using a list instead of the "rest" argument because rest argument makes implementing procedures like the following difficult:

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

Packing arbitrary number of arguments into a list allows you to perform any list operation on the arguments. You can do more with less syntax and confusion. Here is my Scheme version of average that take 'n' arguments:

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

Here is the same procedure in Common Lisp:

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


回答5:

In Scheme R5RS:

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