Сегодня развлекался с моим любимым 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 вообще? :)