Выполнение динамично связанной функции в Clojure

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

Это работает как запланировано на функции, определяемые при уровне пространства имен с 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 quoted звонит в функции для работы на функции, определяемые в другой функции?


Обновление:

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

(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. Это кажется намного более изящным и "шепелявость-y". Однако каждая из этих функций должна получить доступ к моим доменным данным (названный vars), и эти данные продолжают изменяться как прогоны программы. Я не хотел портить свои правила путем представления дополнительного аргумента, таким образом, я хотел vars быть доступным eval'd функционирует через динамический обзор.

Я теперь узнал, что динамический обзор может быть сделан с помощью binding, но этому также нужен a declare.

5
задан Community 23 May 2017 в 12:21
поделиться

2 ответа

Вы имеете в виду что-то вроде этого?

(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, ср. the CLHS :

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

5
ответ дан 14 December 2019 в 04:34
поделиться

Я думаю, вы решаете не ту задачу. В функциональных языках функции являются значениями и могут быть назначены всему, что может хранить любое другое значение, например карта. Вы не должны пытаться манипулировать пространствами имен или исключать что-либо - это не 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
3
ответ дан 14 December 2019 в 04:34
поделиться
Другие вопросы по тегам:

Похожие вопросы: