over 5 years ago

Сегодня развлекался с моим любимым Common Lisp и обнаружил интересную деталь.

Допустим, мы хотим написать простую функцию для определения среднего арифметического
набора заданных чисел. Чтобы она работала вот так:

> (average 1 2 3 4) 
> 5/2

...ну хорошо, хорошо:

> (coerce (average 1 2 3 4) 'float) 
> 2.5

Заметьте то, что я не хочу передавать в average список, я хочу кортеж чисел.
Интересная деталь вот в чём: самая простая (в смысле, лаконичная) реализация,
которая пришла мне в голову, это такая:

(defun average (&rest args) 
  (/ (reduce #'+ args) (list-length args)))

Дело в том, что определение среднего арифметического — это задачка для средней
школы, и мы вполне можем её дать в качестве задания по программированию на
второй паре (на первой мы изучим весь синтаксис лиспа вместе с макросами defun
и defvar). Но на самом деле мы не можем её дать, потому что у нас есть вот
эта форма в решении: (reduce #'+ args), и, чтобы студенты смогли ей бегло
пользоваться, нужно, чтобы они знали как минимум:

  • о разделении пространства имён переменных и пространства имён функций;
  • как передавать в функцию имя другой функции;
  • о назначении и работе reduce, что для не знакомого с функциональными методами, вообще говоря, стопроцентная магия (особенно если их уже обработали алголоподобными языками);
  • о том, что параметры, переданные через &rest, оборачиваются в список.

Не то, чтобы я здесь докапывался до Лиспа, но всё же факт интересный: организация
этого языка такова, что для решения подобной простой задачи нам требуется знать
пол-языка и ещё минимум одну функциональную концепцию.

Конечно, мы можем соорудить нечто работающее из let и dotimes, но тогда
зачем нам Common Lisp вообще? :)

 
comments powered by Disqus