2010-07-14 10 views
7

Sto solo giocando con lo schema/lisp e stavo pensando a come avrei corretto la mia definizione di average. Non sono sicuro di come fare alcune cose che penso siano necessarie.(define (average ....)) in Lisp

  • definire una procedura che richiede un numero arbitrario di argomenti
  • contano tali argomenti
  • passare l'elenco argomento (+) per sommare insieme

Qualcuno ha un esempio di definizione average? Non sembra che conosca abbastanza LISP per formare una ricerca sul Web che recuperi i risultati che sto cercando.

+0

Questo è s non fare i compiti (è stato svalutato per qualche motivo ... probabilmente perché qualcuno pensava che fosse). Sto lavorando attraverso il corso SICP online e stavo solo giocando con LISP chiedendomi come creare questo tempo di procedura. –

+1

(FWIW, questo era abbastanza chiaro - quasi tutti i corsi evitano di andare oltre le funzioni con argomenti di resto.) –

risposta

11

La definizione sarebbe molto semplice one-liner, ma senza rovinare esso, si dovrebbe guardare in:

  • un "riposo" argomento - questo (define (foo . xs) ...xs...) definisce foo come una funzione che prende un numero qualsiasi di argomenti e sono disponibili come un elenco che sarà il valore di xs.

  • length restituisce la lunghezza di un elenco.

  • apply accetta una funzione e un elenco di valori e applica la funzione a questi valori.

Quando si ottiene che, si può andare per di più:

  • vedere la funzione foldl per evitare di applicare un elenco su un potenzialmente molto grande lista (questo può importare in alcune implementazioni in cui la lunghezza dell'elenco degli argomenti è limitato, ma non farebbe molta differenza in Racket).

  • nota che Racket ha razionali esatti e che è possibile utilizzare exact->inexact per creare una versione a virgola mobile più efficiente.





E gli spoiler sono:

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

  • Make it richiede il e argomento: (define (average n . ns) (/ (apply + n ns) (add1 (length ns))))

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

  • Ne fanno uso in virgola mobile: (define (average n . ns) (/ (foldl + 0.0 (cons n ns)) (add1 (length ns))))

3

In Common Lisp, sembra che si può fare:

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

anche se non ho idea se &rest è disponibile su tutte le implementazioni di Lisp. Riferimento here.

Mettendo codice in risultati GNU CLISP in:

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

che è 3,5 (corretto).

+0

Non si deve presumere che un'implementazione lisp possa accettare un numero qualsiasi di input (e IIRC, CLisp si interromperà). Inoltre, la funzione dovrebbe sempre restituire un numero - questo codice restituirà NIL quando non viene fornito alcun input. –

+0

@Eli, probabilmente conosci meglio LISP di me (come dimostra la tua risposta molto più completa). Ma non sono d'accordo su quest'ultima affermazione. Matematicamente, la media di un insieme nullo non è un numero, non è definita. Se vuoi che restituisca 0 {ugh! IMNSHO :-)}, puoi modificare il codice per farlo. – paxdiablo

+0

paxdiablo: a destra, * è * indefinito, e la traduzione di quello in codice è nella maggior parte dei casi meglio eseguita generando un errore. Vedere il mio codice per esempio: genererà un errore se non viene fornito alcun input (una divisione errata per zero nella prima versione e un errore di arity molto migliore in seguito). Naturalmente ci sono anche casi in cui si desidera restituire un risultato "non definito" ('null',' undefined', 'nan', ecc.), Ma * di solito * è meglio far sì che il codice passi un errore prima, piuttosto che permettendogli di propagare un risultato potenzialmente falso attraverso. –

1

Nello Schema, io preferisco usare una lista al posto del "riposo" l'argomento in quanto argomento di riposo rende difficili le procedure di implementazione:

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

L'inserimento di un numero arbitrario di argomenti in un elenco consente di eseguire qualsiasi operazione di elenco sugli argomenti. Puoi fare di più con meno sintassi e confusione. Ecco la mia versione del sistema di average che prendono 'n' argomenti:

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

Ecco la stessa procedura in Common Lisp:

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

È possibile utilizzare 'apply' per tradurre la lista in una lista di argomenti. – Zorf

+0

'(Incf sum n)' è migliore di '(setf sum (+ sum n))'. Inoltre, il mio primo prototipo sarebbe di solito '(/ (reduce # '+ lista) (lista lunghezza))', e quindi potrei cambiarlo in un '(ciclo per elemento in lista conteggio elemento in lunghezza somma elemento in somma infine (return (/ sum length))) '. – Svante

+0

Non potresti invece scrivere una semplice macro per decomprimere l'elenco come singoli argomenti per la chiamata di 'average' in modo che assomigli a:' (define (call-average. Ns) (average (unpack ns))) '? Sono attualmente un principiante Scheme/Lisp, quindi sinceramente non so quanto sia possibile. – eestrada

2

Due versioni 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))) 
1

Nello schema R5RS:

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