вторник, 8 мая 2012 г.

Гусеничный Lisp-трактор

Просматривая записи Казимира Майорника, нашел у него очень любопытную статью. Это всего лишь забавный эксперимент, показывающий, как при помощи лисповой парадигмы "код-как-данные" можно реализовать циклическое выполнение кода без циклов и рекурсий. Никакой практической ценности в этом нет, просто красиво смотрится. Я написал что-то похожее на Clojure, но несколько усовершенствованное. В моей версии алгоритм выглядит так.

1) Список f содержит всего одну функцию. Её и запускаем первой.
2) Первая функция передает свой код в update.
3) Update "препарирует" код, находит номер текущей итерации, инкрементирует его и собирает такую же функцию, как ей передали, но с новым значением итерации.
4) Первая функция получает вторую функцию и добавляет её в конец списка f. Если список f содержит более трех элементов, то первый элемент списка f удаляется. Таким образом, в списке f содержится не более четырех элементов.
5) Первая функция запускает вторую функцию и завершается.

Выглядит и впрямь, как гусеничный трак :-)

(defn update [func]
  (let [p (nth func 2)        ;; Получить список (println (str "Hi for the " 1 " time!"))
        n (nth (second p) 2)  ;; Получить 1 из списка
        ;; Создать список (println (str "Hi for the " 2 " time!"))
        new-p (cons 'println (list (map #(if (= n %) (inc n) (identity %)) (second p))))]
    ;; Заменить в теле функции (println (str "Hi for the " 1 " time!"))
    ;; на (println (str "Hi for the " 2 " time!"))
    (map #(if (and (seq? %) (= (first %) 'println)) new-p (identity %)) func)))

(def f (ref ['(fn []
                (println (str "Hi for the " 1 " time!"))
                (let [func (last @f)
                      ;; Обновить список функций f
                      ;; Удалить первую функцию, если список f содержит больше трех элементов
                      new-f (if (> (count @f) 3)
                              ;; Добавить обновленную функцию в конец списка
                              (conj (vec (drop 1 @f)) (update func)) 
                              (conj @f (update func)))]
                  (dosync (ref-set f new-f))
                  ;; Выполнить последнюю функцию списка
                  ((eval (last @f)))))]))

((eval (last @f)))

Комментариев нет:

Отправить комментарий