Генерация кода в 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))

С помощью макросов вам не нужно сериализовывать макроэкспонирование; компилятор будет использовать его автоматически.