Clojure выполнение отложенных функций
Хорошо Вот что я пытаюсь сделать
(defn addresses [person-id]
;addresses-retrival )
(defn person [id]
(merge {:addresses (addresses id)} {:name "john"}))
В функции выше человека я хочу, чтобы адреса извлекались только по требованию, например, только когда я делаю
(:addresses (person 10))
а не когда
(person 10)
Я не уверен, что я иду об этом праве, новичок в clojure.
Ответы
Ответ 1
Вы можете использовать задержку.
(defn person [id]
(delay {:addresses (addresses id) :name "john"}))
(человек 2) затем вернет задержку, не оценивая ничего.
Чтобы получить доступ к содержимому и оценить задержанный объект, используйте команду force или deref (или @).
(:addresses @(person 5))
В качестве альтернативы вы можете поместить задержку только по адресу.
(defn person [id]
{:addresses (delay (addresses id)) :name "john"})
который может быть лучше в зависимости от вашей проблемы.
Это позволяет определить:
(defn get-address [person]
@(:address person))
Который получит задержанный адрес и заставит его.
(Forcing означает вычисление в первый раз и получение принудительного результата в любое другое время).
Ответ 2
По крайней мере, насколько последовательности идут, clojure довольно проклят ленивый, не требуя, чтобы ему сказали.
Здесь, моделируя ваш адрес-поиск как подсчет, попробуйте:
(defn addresses [person-id]
(iterate #(do (println %) (inc %)) person-id))
(defn person [id]
(merge {:addresses (addresses id)} {:name "john"}))
(def people (map person (range 100)))
Пока он ничего не печатает, но если вы скажете:
(doall (take 5 (:addresses (nth people 10))))
Затем вы должны увидеть, что печать происходит именно в тех случаях, которые должны произойти, чтобы подсчитать пять на десятом месте. Я бы предположил, что это может быть поведение, которое вы хотите?
Итак, получите ваш адресный поиск, чтобы создать ленивую последовательность (карта, фильтр, уменьшить все будет)
Ответ 3
Вы можете вернуть функцию из функции addresses
, которая при последующем вызове будет получать адреса. Что-то вроде этого:
(defn addresses [person-id]
#(;addresses-retrival))
(defn person [id]
(merge {:addresses ((addresses id))} {:name "john"}))
Примечание. Функция addresses
возвращает анонимную функцию (созданную с помощью #
), а функция person
вызывает эту анонимную функцию, используя дополнительную пару парнеров.
Ответ 4
Я могу предложить что-то близкое к тому, что вы ожидаете.
; Note the use of anonymouns function. #(addresses id)
(defn person [id]
(merge {:addresses #(addresses id)} {:name "john"}))
; :addresses returns a function. Evaluate it by wrapping it in another set of parans.
((:addresses (person 10)))
Ответ 5
Помните, что задержки запомнены, поэтому последовательные вызовы задержки ваших адресов всегда будут иметь тот же адрес, что и при первом изменении задержки.
(defn addresses [person-id]
{:home (str (rand-int 100) " Cool St.") :work "1243 Boring St."})
(defn person [id]
(merge {:addresses (delay (addresses id))} {:name "john"}))
(let [person1 (person 1)]
(println @(:addresses person1))
(println @(:addresses person1)))
Это напечатает:
{:home 65 Cool St., :work 1243 Boring St.}
{:home 65 Cool St., :work 1243 Boring St.}
Обратите внимание на то, что домашний адрес не изменяется во второй части задержки.
Если вы не хотите этого поведения, вам нужно использовать закрытие функции.
(defn addresses [person-id]
{:home (str (rand-int 100) " Cool St.") :work "1243 Boring St."})
(defn person [id]
(merge {:addresses (fn [] (addresses id))} {:name "john"}))
(let [person1 (person 1)]
(println ((:addresses person1)))
(println ((:addresses person1))))
Это напечатает:
{:home 16 Cool St., :work 1243 Boring St.}
{:home 31 Cool St., :work 1243 Boring St.}
Обратите внимание на то, как домашний адрес был другим при последующем вызове закрытия.
Итак, если вы addresses
функция выполняет побочный эффект, скажем, извлекает адреса из базы данных. И люди могут изменить свои адреса, и вы хотите, чтобы ваш код всегда имел самый последний адрес, что-то нужно иметь в виду, если Delay работает для вас или если закрытие функции будет лучшим кандидатом.