Генерация кода в Clojure
(Отказ от ответственности: я парень С#. Я только начал изучать Clojure.)
Я понимаю, что программа Clojure имеет возможность манипулировать собой или легко создавать другие программы. Это как-то связано со всем, что в Clojure является структурой данных, и что создание программ будет таким же, как создание любого другого типа структуры данных.
Есть ли у кого-нибудь хорошая примерная программа (или ссылка на нее), которая показывает это?
Если вы создаете программу, можете ли вы "сериализовать" эту программу на диск для последующего выполнения?
Только для справки:
-
Я пытаюсь играть с генетическим программированием. Я хочу генерировать множество небольших программ, оценивать их и использовать успешные, чтобы создавать больше программ. Подробнее здесь и здесь.
-
Я думаю, что здесь неправильно использую условия. По программе я фактически имею в виду список Clojure, а по коду Generation я имею в виду "Генерация списков". Мне просто нужен список, чтобы содержать фактические вызовы функций и параметры. Мне нужно будет иметь возможность контролировать, когда этот список будет "выполнен".
Ответы
Ответ 1
Рассмотрим (+ 1 2)
. В качестве данных это связанный список из трех элементов: Символ +
и два целых числа. Как код, это вызов функции, говорящий "Вызвать функцию с именем +
с этими двумя целыми числами в качестве аргументов и дать мне результат". Вы можете сделать что-нибудь в этом списке, что вы можете сделать с любым другим списком данных. Вы также можете eval
получить результат.
user> (def x '(+ 1 2))
#'user/x
user> (first x)
+
user> (rest x)
(1 2)
user> (map class x)
(clojure.lang.Symbol java.lang.Integer java.lang.Integer)
user> (reverse x)
(2 1 +)
user> (concat x (rest x))
(+ 1 2 1 2)
user> (eval x)
3
user> (defn foo []
(let [ops '[+ - * /] ; SO lisp-highlighting sucks
nums (repeatedly #(rand-int 5))
expr (list* (rand-elt ops) (take 10 nums))]
(prn expr)
(prn (eval expr))))
user> (foo)
(+ 4 1 0 3 2 3 4 3 1 2)
23
nil
user> (foo)
(- 1 3 2 2 1 2 1 4 0 1)
-15
nil
Ответ 2
Clojure - это LISP, а это означает, что это homoiconic язык: отсутствует структурное различие между данными и код. Его списки полностью вниз. Он также имеет расширяемый компилятор, который позволяет расширять синтаксис с помощью макросов. Но неясно из вашего заявления о том, что вам действительно нужна такая вещь.
В основном вы запускаете код, который генерирует списки (которые являются действительно следующими ген-программами), сохраняя их, а затем запуская новые программы. Если ваша эволюция поколений не потребует нового синтаксиса, вам, вероятно, не нужно будет прибегать к макросам.
Ответ 3
Нашел неполный ответ в эту статью:
Функции pr
и prn
похожи на их печатные и печатные копии, но их выход находится в форме, которая может читайте читателем Clojure. Oни подходят для сериализации Clojureструктуры данных. По умолчанию они делают не печатать метаданные. Это может быть изменено путем привязки специального символа *print-meta*
до true
.
Это, по крайней мере, отвечает на вторую часть моего вопроса.
Ответ 4
Вопрос несколько вводит в заблуждение, так как Clojure также выполняет "генерации кода" на лету, когда он компилирует исходный код Clojure в байт-код Java.
В этом конкретном случае, я считаю, что вам интересны макросы Lisp в частности. Я думаю, это может быть интересно:
Clojure сама документация
Видео, Макросы (в Clojure) за 20 минут
Стандартная проблема: Википедия - Clojure
Обратите внимание, что макросы в Clojure работают очень похожими на Common Lisp макросы (тип-2 lisp) и не совсем так же похожи на макросы Scheme.
Счастливое кодирование.
Ответ 5
Взгляните на макросы. Например,
(defmacro defmacro-
"Same as defmacro but yields a private definition"
[name & decls]
(list* `defmacro (with-meta name (assoc (meta name) :private true)) decls))
С помощью макросов вам не нужно сериализовывать макроэкспонирование; компилятор будет использовать его автоматически.