Как вернуть вывод рекурсивной функции в Clojure - PullRequest
3 голосов
/ 07 мая 2010

Я новичок в функциональных языках и clojure, поэтому, пожалуйста, потерпите меня ...

Я пытаюсь создать список функций со случайными параметрами или константами. Функция, которая создает список функций, уже работает, хотя она не возвращает саму функцию. Я подтвердил это с помощью println.

(редактировать: ладно, в конце концов, он еще не работает правильно)

(редактировать: теперь он работает, но он не может быть "вычислен"). Мне кажется, мне нужно повториться как минимум два раза, чтобы убедиться, что есть хотя бы два дочерних узла. Возможно ли это? 1008 *

Вот фрагмент:

(def operations (list #(- %1 %2) #(+ %1 %2) #(* %1 %2) #(/ %1 %2)))
(def parameters (list \u \v \w \x \y \z))
(def parameterlistcount 6)
(def paramcount 2)
(def opcount 4)

(defn generate-function


([] (generate-function 2 4 0.5 0.6 () parameters))
  ([pc maxdepth fp pp function-list params]
     (if (and (pos? maxdepth) (< (rand) fp))
       (let [function-list
             (conj function-list
                    (nth operations
                         (rand-int (count operations))))]
         (recur pc (dec maxdepth) fp pp function-list params))
       (if (and (< (rand) pp) (pos? pc))
         (let [ params (pop parameters)
        function-list
               (conj function-list
                      (nth params
                         (rand-int (count params))))]
       (if (contains? (set operations) (last function-list) )
          (recur (dec pc) maxdepth fp pp function-list params)
          nil))
         (let [function-list
               (conj function-list
                      (rand-int 100))]
           (if (or (pos? maxdepth) (pos? pc))
          (if (contains? (set operations) (last function-list) )
        (recur pc maxdepth fp pp function-list params)
        nil)
          function-list))))))

Любая помощь будет оценена, спасибо!

Ответы [ 3 ]

3 голосов
/ 07 мая 2010

Вот мой шанс переписать вашу функцию (см. Комментарии ниже):

(defn generate-function
  ([] (generate-function 2 4 0.5 0.6 ()))
  ([pc maxdepth fp pp function-list]
     (if (and (pos? maxdepth) (< (rand) fp))
       (let [function-list
             (conj function-list
                   {:op
                    (nth operations
                         (rand-int (count operations)))})]
         (recur pc (dec maxdepth) fp pp function-list))
       (if (and (< (rand) pp) (pos? pc))
         (let [function-list
               (conj function-list
                     {:param
                      (nth parameters
                           (rand-int (count parameters)))})]
           (recur (dec pc) maxdepth fp pp function-list))
         (let [function-list
               (conj function-list
                     {:const
                      (rand-int 100)})]
           (if (or (pos? maxdepth) (pos? pc))
             (recur pc maxdepth fp pp function-list)
             function-list))))))

И несколько примеров использования из моего REPL ...

user> (generate-function)
({:const 63} {:op #<user$fn__4557 user$fn__4557@6cbb2d>} {:const 77} {:param \w} {:op #<user$fn__4559 user$fn__4559@8e68bd>} {:const 3} {:param \v} {:const 1} {:const 8} {:op #<user$fn__4559 user$fn__4559@8e68bd>} {:op #<user$fn__4555 user$fn__4555@6f0962>})
user> (generate-function)
({:const 27} {:param \y} {:param \v} {:op #<user$fn__4561 user$fn__4561@10463c3>} {:op #<user$fn__4561 user$fn__4561@10463c3>} {:op #<user$fn__4561 user$fn__4561@10463c3>} {:op #<user$fn__4561 user$fn__4561@10463c3>} {:const 61})

Несколько вещей, которые нужно иметь в виду, в довольно случайном порядке:

  1. Я использовал recur в приведенном выше, чтобы избежать использования стека при рекурсивных самостоятельных вызовах. Тем не менее, у вас есть это dotimes утверждение, которое заставляет меня задуматься о том, может ли вас заинтересовать создание группы function-list параллельно с одним вызовом generate-function. Если это так, то хвостовая рекурсия с recur может не подходить для такого упрощенного кода, как этот, поэтому вы можете либо согласиться на регулярные самостоятельные вызовы (но рассмотрите возможность достижения предела рекурсии; если вы уверены, что вы будете генерировать только небольшие функции, и это не будет проблемой, продолжайте самостоятельные вызовы) или исследуйте стиль передачи продолжения и переписайте свою функцию в этом стиле.

  2. Элемент (do (dec pc) ...) в вашем коде ничего не делает со значением pc в следующем рекурсивном вызове или с его текущим значением. Локальные переменные (или локальные, как их чаще всего называют в сообществе) в Clojure неизменны; это включает в себя параметры функции. Если вы хотите передать уменьшенную pc какой-либо функции, вам нужно будет сделать это, как вы это делали с maxdepth в более ранней ветви вашего кода.

  3. Я переименовал вашу функцию в generate-function, потому что регистр верблюдов в именах функций довольно необычен на земле Clojure. Кроме того, я переименовал параметр, который вы назвали, с function на function-list (поэтому, возможно, мне следовало бы использовать имя типа generate-function-list для функции ... хм), потому что это то, что есть на данный момент.

  4. Обратите внимание, что нет смысла хранить отдельный opcount Var; Постоянные списки Clojure (создаваемые функцией list) несут в себе счет, поэтому (count some-list) - это операция с постоянным временем (и очень быстрая). Кроме того, было бы идиоматично использовать векторы для operations и parameters (и вы можете переключаться на векторы, не меняя ничего в остальной части кода!). Например. [\u \v \w \x \y \z].

  5. В Clojure 1.2 вы сможете использовать (rand-nth coll) для (nth coll (rand-int (count coll))).

  6. Если вы хотите генерировать фактические функции Clojure из деревьев элементов, представляющих операции, параметры и константы, вам нужно будет использовать eval. Это не рекомендуется в большинстве сценариев, но не для эволюционного программирования и подобных вещей, где это единственный путь.

  7. Лично я бы использовал другой формат карт операций / параметров / констант: что-то вроде {:category foo, :content bar}, где foo это :op, :param или :const и bar это что-то подходящее в связи с любым данным foo.

2 голосов
/ 07 мая 2010

В целом, в Clojure лучше использовать (recur ...) для своих рекурсивных функций. Из документации: «Обратите внимание, что recur - это единственная циклическая конструкция, не потребляющая стек, в Clojure». ссылка

Еще одна вещь, на которую следует обратить внимание, это то, что вы можете вызвать рандомизатор вне рекурсивной функции, чтобы вы могли определить условие остановки внутри функции.

Так вот так:

(let [n (rand-int 10)] 
  (println "Let's define f with n =" n)
  (defn f [x] 
    (if (> x n) 
      "result" 
      (do (println x) 
          (recur (inc x))))))

Он печатает:

Let's define f with n = 4

user> (f 0)
0
1
2
3
4
"result"

где 4, конечно, случайное число от 0 (включительно) до 10 (исключая).

1 голос
/ 11 мая 2010

Итак, я обнаружил, что поступил неправильно. Рекурсивное определение дерева - это не что иное, как определение вершин и попытка связать все с ним. Итак, я придумал это менее чем за 15 минут. > _ <</p>

(defn generate-branch
"Generate branches for tree"
  ([] (generate-branch 0.6 () (list \x \y \z)))
  ([pp branch params]
      (loop [branch
        (conj branch (nth operations (rand-int (count operations))))]
    (if (= (count branch) 3)
      branch
      (if (and (< (rand) pp))
        (recur (conj branch (nth params (rand-int (count params)))))
        (recur (conj branch (rand-int 100))))))))

(defn build-vertex
"Generates a vertex from branches"
  []
  (let [vertex (list (nth operations (rand-int (count operations))))]
    (conj vertex (take 5 (repeatedly generate-branch)))))

СПАСИБО ВСЕМ!

...