Выполнение динамически связанной функции в Clojure - PullRequest
5 голосов
/ 04 апреля 2010

Я бы хотел предварительно сохранить несколько вызовов функций в структуре данных, а затем оценить / выполнить их из другой функции.

Это работает, как и планировалось, для функций, определенных на уровне пространства имен с defn (хотя определение функции приходит после того, как я создал структуру данных), но не будет работать с функциями, определенными let [name (fn или letfn внутри функции.

Вот мой небольшой автономный пример:

(def todoA '(funcA))
(def todoB '(funcB))
(def todoC '(funcC))
(def todoD '(funcD)) ; unused

(defn funcA [] (println "hello funcA!"))

(declare funcB funcC)

(defn runit []
    (let [funcB (fn [] (println "hello funcB"))]
    (letfn [(funcC [] (println "hello funcC!"))]
        (funcA)       ; OK
        (eval todoA)  ; OK
        (funcB)       ; OK
        (eval todoB)  ; "Unable to resolve symbol: funcB in this context" at line 2
        (funcC)       ; OK
        (eval todoC)  ; "Unable to resolve symbol: funcC in this context" at line 3
)))

В случае, если вам интересно узнать о моей настройке теста, чтобы увидеть результат этих 6 операторов, я комментирую / раскомментирую специфичные для OK / неудачные строки, а затем вызываю (runit) из REPL.

Есть ли какое-то простое исправление, которое я мог бы предпринять, чтобы получить eval 'd quote d вызовов функций для работы для функций, определенных внутри другой функции?


Обновление:

Это (по предложению Данлея) работает .Давайте посмотрим, смогу ли я заставить этот метод работать в «реальной жизни!»

(def todoB '(funcB))
(declare funcB)

(defn runit []
  (binding [funcB (fn [] (println "hello funcB"))]
    (funcB)
    (eval todoB)  ; "Unable to resolve symbol: funcB in this context" at line 1!
))

Обновление:

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

Я использовал для определения ограничений в виде набора простых векторов, например:

[:con-eq :spain :dog]
[:abs-pos :norway 1]
[:con-eq :kools :yellow]
[:next-to :chesterfields :fox]

, где первый из каждого вектора будет указывать вид ограничения.Но это привело меня к неловкой реализации механизма диспетчеризации этих правил, поэтому я решил вместо этого закодировать их как (цитируемые) вызовы функций:

'(coloc :japan :parliament) ; 10
'(coloc :coffee :green) ; 12
'(next-to :chesterfield :fox) ; 5

, чтобы я мог отправить ограничивающее правило с помощью простого eval.Это выглядит намного более элегантно и «шутливо».Однако каждой из этих функций требуется доступ к данным моего домена (с именем vars), и эти данные постоянно меняются по мере выполнения программы.Я не хотел портить свои правила, вводя дополнительный аргумент, поэтому я хотел, чтобы vars был доступен для функций eval d через динамическую область видимости.

Теперь я узнал, что динамическая область видимостиможно сделать с помощью binding, но для этого также нужно declare.

Ответы [ 2 ]

5 голосов
/ 04 апреля 2010

Вы имеете в виду что-то подобное?

(def foo '(bar))
(declare bar)

(binding [bar (fn [] (println "hello bar"))]
  (eval foo))

Если да, ваша проблема сводится к следующему:

(let [foo 1]
  (eval 'foo))

Это не будет работать, потому что eval не оценивает влексическая среда.Вы можете обойти это, используя vars:

(declare foo)

(binding [foo 1]
  (eval 'foo))

Что касается этого, Clojure, похоже, имеет семантику, аналогичную CL, ср. CLHS :

Оценивает форму в текущей динамической среде и нулевой лексической среде.

3 голосов
/ 06 апреля 2010

Я думаю, вы решаете не ту проблему. В функциональных языках функции являются значениями и могут быть назначены любому, что может хранить любое другое значение, например, карта. Вы не должны пытаться манипулировать пространствами имен или ничего оценивать - это не perl.

Попробуйте что-то вроде этого и используйте Assoc для локального изменения карты:

user=> (def fnmap {:funcA (fn [x] (inc x)), :funcB (fn [x] (* x 2))})
#'user/fnmap
user=> ((:funcA fnmap) 10)
11
user=> ((:funcB fnmap) 10)
20
...