2010-07-14 2 views
7

Я просто играю со схемой/lisp и думал о том, как я буду править своим собственным определением average. Я не уверен, как сделать некоторые вещи, которые, как мне кажется, необходимы.(define (average ....)) in Lisp

  • определить процедуру, которая принимает произвольное число аргументов
  • подсчитывать эти аргументы
  • проходит список аргументов в (+), чтобы подвести их вместе

ли кто-то есть пример определения average? Я, кажется, не знаю достаточно о LISP, чтобы сформировать веб-поиск, который возвращает результаты, которые я ищу.

+0

Это i а не домашнее задание (по какой-то причине его пресекали), вероятно, потому, что кто-то думал, что это так). Я работаю через курс SICP онлайн, и я просто играл с LISP, задаваясь вопросом, как создать это время процедуры. –

+1

(FWIW, это было довольно ясно - почти все курсы избегают перебора функций с аргументами отдыха.) –

ответ

11

определение было бы очень просто один лайнер, но не испортив его, вы должны смотреть в:

  • «отдых» аргумент - это (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))))

3

В Common Lisp, это выглядит, как вы можете сделать:

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

хотя я понятия не имею, если &rest доступен на все реализации Лиспа. Ссылка here.

Ввод этого кода в результаты GNU CLISP в:

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

, который 3,5 (правильный).

+0

Вы не должны предполагать, что реализация lisp может принимать любое количество входов (и IIRC, CLisp будет ломаться). Кроме того, функция всегда должна возвращать номер - этот код вернет NIL, если не будет введен никаких входов. –

+0

@Eli, вы, вероятно, знаете больше о LISP, чем я (о чем свидетельствует ваш гораздо более полный ответ). Но я должен не согласиться с этим последним заявлением. Математически среднее значение нулевого набора не является числом, оно не определено. Если вы хотите, чтобы он вернул 0 {тьфу! IMNSHO :-)}, вы можете изменить код, чтобы сделать это. – paxdiablo

+0

paxdiablo: right, it * is * undefined, а перевод этого кода в большинстве случаев лучше всего сделать, выбросив ошибку. Например, см. Мой код: он выдает ошибку при отсутствии входных данных (плохое деление на ноль в первой версии и гораздо лучшая ошибка arity позже). Конечно, есть также случаи, когда вы хотите вернуть какой-то «неопределенный» результат («null», «undefined», «nan» и т. Д.), Но * обычно * лучше сделать код, который выдает ошибку раньше, а не позволяя ему распространять потенциально фиктивный результат. –

1

В схеме, я предпочитаю использовать список вместо «остальное» аргумент, потому что аргумент остальные делает следующие процедуры осуществления, такие как следующие:

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

Упаковка произвольного количества аргументов в список позволяет выполнять любую операцию списка по аргументам. Вы можете делать больше с меньшим синтаксисом и путаницей. Вот моя схема версии average, которые принимают 'п' аргументы:

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

Вы можете использовать 'apply' для перевода списка в список аргументов. – Zorf

+0

'(Incf sum n)' лучше, чем '(setf sum (+ sum n))'. Кроме того, моим первым прототипом обычно будет '(/ (сокращение # '+ list) (список длины)), а затем я могу изменить его на' (цикл для элемента в элементе подсчета списка в элемент суммирования длины в сумму окончательно (return (/ sum length))). – Svante

+0

Не могли бы вы вместо этого написать простой макрос, чтобы распаковать список в виде отдельных аргументов для вызова 'average', чтобы он выглядел так:' (define (call-average. Ns) (average (unpack ns))) '? Я в настоящее время новичок Scheme/Lisp, поэтому, честно говоря, я не знаю, насколько это возможно. – eestrada

2

Две версии в 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

На схеме R5RS:

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