Tuesday, December 7, 2010

clisp && sbcl

Всё-таки множество интерпретаторов/компиляторов языка — это замечательно.

Изначально я установил только CLISP и довольно долго был уверен, что этого мне будет достаточно. Ан нет: эта штука генерирует весьма тормозной байткод. Для работы в интерактивном режиме лучше CLISP ничего не придумаешь, а вот для исполнения программ, связанных с перебором... это полная жуть. Казалось бы, чего тут такого — перебрать все перестановки 10 элементов? Каких-то там 3628800 итераций, фи. А на CLISP этот перебор отнял секунд 15 времени (и это при том, что я несколько советов из книжки "ANSI Common LISP" для оптимизации заюзал). Жуть.

В общем, поставил я SBCL. Результаты превзошли все ожидания: большинство написанных мной функций стали выполняться быстрее раз эдак в 10-15 и теперь по скорости сравнимы с сишным кодом. Единственное, что пришлось поменять в коде, так это вызовы функций, связанных с регулярными выражениями (установил cl-ppcre). Ну оно и к лучшему, PCRE рулят =) (В CLISP встроены только POSIX-регулярки, насколько я понял.)

Monday, December 6, 2010

В продолжение обзора ЯП

К чему приходит тот, кого во всех языках достаёт ограниченность синтаксиса? Правильно, к Common Lisp-у =) Единственное, чего я опасаюсь, так это того, что буду строчить на нём всякую невменяемую хрень, потому что то, что язык позволяет выразить на нём всё что вздумается, не всегда полезно. Потому что вздуматься может всякое. Вот сегодняшний пример: надо было цепную дробь вычислить (на projecteuler.net зарегистрировался ради того, чтобы было на чём новый язык поизучать). Взбрела в голову шальная мысль: а что, если не сразу вычислять, а сперва выражение составить? Ну и набыдлокодил в итоге:

(defun generate-continued-fraction (lst)
  (loop with sexp = (list '+ (first lst) (list '/ 1 (second lst)))
        for num in (rest (rest lst)) do
        (loop for to-change = sexp then (third to-change)
              until (atom (third to-change))
              finally (setf (third to-change)
                            (list '+ (third to-change) (list '/ 1 num))))
        finally (return sexp)))

Ну а чё, работает же даже:

(generate-continued-fraction '(2 1 2 1 1 4 1))
(+ 2 (/ 1 (+ 1 (/ 1 (+ 2 (/ 1 (+ 1 (/ 1 (+ 1 (/ 1 (+ 4 (/ 1 1))))))))))))

Но вот о производительности такой функции лучше не думать, особенно с учётом того, что потом ещё и eval вызывается. На любом обычном языке мне б и в голову, наверное, не взбрело подобный бред писать, написал бы что-нибудь навроде
(defun continued-fraction (lst)
  (loop with rlst = (reverse lst)
        with frac = (pop rlst)
        for num in rlst do
          (setf frac (+ num (/ 1 frac)))
        finally (return frac)))

Так что надо порядок в голове наводить... %-)