2010-07-14 40 views
7

Estoy jugando con scheme/lisp y estaba pensando en cómo corregir mi propia definición de average. Aunque no estoy seguro de cómo hacer algunas cosas que creo que son necesarias.(define (promedio ...)) en Lisp

  • definir un procedimiento que toma un número arbitrario de argumentos
  • cuentan esos argumentos
  • pase la lista de argumentos para (+) para sumar juntos

¿Alguien tiene un ejemplo de definición average? No parece saber lo suficiente sobre LISP para formar una búsqueda web que recupera los resultados que estoy buscando.

+0

Este i No es tarea (se votó por alguna razón ... probablemente porque alguien pensó que era). Estoy trabajando en el curso de SICP en línea y estaba jugando con LISP preguntándome cómo crear este tiempo de procedimiento. –

+1

(FWIW, esto fue bastante claro: casi todos los cursos evitan revisar funciones con argumentos de descanso.) –

Respuesta

11

La definición sería una forma muy sencilla de una sola línea, pero sin estropearlo, usted debe buscar en:

  • un "descanso" argumento - esto (define (foo . xs) ...xs...) define foo como una función que toma un número cualquiera de argumentos y están disponibles como una lista que será el valor de xs.

  • length devuelve la longitud de una lista.

  • apply toma una función y una lista de valores y aplica la función a estos valores.

Al llegar eso, se puede ir por más:

  • ver la función foldl evitar la aplicación de una lista en una lista potencialmente muy grande (esto puede importar en algunas implementaciones donde la longitud de la lista de argumentos es limitada, pero no haría mucha diferencia en Racket).

  • tenga en cuenta que Racket tiene racionales exactos, y puede usar exact->inexact para hacer una versión más eficiente de coma flotante.





Y los alerones son:

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

  • Hacer que requiere el e argumento: (define (average n . ns) (/ (apply + n ns) (add1 (length ns))))

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

  • hacer que use coma flotante: (define (average n . ns) (/ (foldl + 0.0 (cons n ns)) (add1 (length ns))))

3

En Common Lisp, parece que se puede hacer:

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

aunque no tengo ni idea de si &rest está disponible en todas las implementaciones de Lisp. Referencia here.

Poner ese código en los resultados de GNU CLISP en:

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

que es 3,5 (correcto).

+0

No debe suponer que una implementación de lisp puede aceptar cualquier cantidad de entradas (y IIRC, CLisp se romperá). Además, la función siempre debe devolver un número: este código devolverá NIL cuando no se le proporcionen entradas. –

+0

@Eli, probablemente sepa más sobre LISP que yo (como lo demuestra su respuesta mucho más completa). Pero tengo que estar en desacuerdo sobre esa última declaración. Matemáticamente, el promedio de un conjunto nulo no es un número, no está definido. Si quieres que vuelva 0 {ugh! IMNSHO :-)}, puede modificar el código para hacerlo. – paxdiablo

+0

paxdiablo: a la derecha, * está * indefinido, y la traducción de ese código se hace en la mayoría de los casos mejor lanzando un error. Ver mi código, por ejemplo: arrojará un error cuando no se den entradas (una mala división por cero en la primera versión, y un error de aridad mucho mejor más adelante). Por supuesto, también hay casos en los que desearía devolver algún resultado "indefinido" ('null',' undefined', 'nan', etc.), pero * generalmente * es mejor hacer que el código arroje un error antes, en lugar de permitiéndole propagar un resultado potencialmente falso a través de. –

1

En el esquema, prefiero utilizar una lista en lugar del "descanso" argumento porque el argumento de descanso hace que los procedimientos de implementación como los siguientes sean difíciles:

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

Empacar un número arbitrario de argumentos en una lista le permite realizar cualquier operación de lista en los argumentos. Puedes hacer más con menos sintaxis y confusión. Aquí está mi versión Esquema de average que tomar 'n' argumentos:

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

aquí es el mismo procedimiento que en Common Lisp:

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

Puede usar 'apply' para traducir la lista a una lista de argumentos. – Zorf

+0

'(Incf sum n)' es mejor que '(setf sum (+ sum n))'. Además, mi primer prototipo normalmente sería '(/ (reducir # '+ lista) (lista de longitud))', y luego podría cambiarlo a '(bucle para elemento en el elemento de conteo de lista en elemento de suma de longitud en suma finalmente (return (/ suma de longitud))) '. – Svante

+0

¿No podría escribir en su lugar una macro simple para descomprimir la lista como argumentos individuales para llamar a 'promedio' para que se vea así:' (define (promedio de llamadas. Ns) (promedio (descomprimir ns))) '? Actualmente soy un novato de Scheme/Lisp, así que honestamente no sé cómo es posible. – eestrada

2

Dos versiones en 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))) 
1

En el esquema R5RS:

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