2015-08-10 6 views
2

Мой вопрос очень похож на этот: How to evaluate a sequence of impure functions in Clojure?, но вместо нечистых функций, как я могу оценить последовательность чистых функций и получить результаты в виде другой последовательности?Как оценить последовательность чистых функций в Clojure

Давайте предположим, что мы имеем вектор функций, таких как:

[#(str "a" "b") #(str "c" "d") #(str "e" "f")] 

И нам нужен выход, как это:

("ab" "cd" "ef") 

Я пытался что-то похожее на это:

(map eval [#(str "a" "b") #(str "c" "d") #(str "e" "f")]) 

, но он просто возвращает вектор ссылок функций.

ответ

8

Существует несколько способов сделать то, что вы просите. Это зависит от того, хотите ли вы ленивую последовательность или нет (вам, вероятно, нужна ленивость, если нет побочных эффектов, но вам может потребоваться отсутствие лени, если есть интенсивное вычисление, которое вы хотите кэшировать), или если вы хотите вектор как выход (то же, что и ваш вход). Я постараюсь следовать тому, что вы пытались сделать.

Ваша карта с Eval делает следующее с каждой Fn:

user=> (eval #(str 1)) 
#<user$eval1332$fn__1333 [email protected]> 

Но вы хотите что-то вроде следующего:

user=> (eval (#(str 1))) 
"1" 

Вы хотите Eval, чтобы иметь сноска применяется, то есть: fn должен быть первым элементом списка. Перечислим его в списке:

user=> (map (comp eval list) [#(str "a" "b") #(str "c" "d") #(str "e" "f")]) 
("ab" "cd" "ef") 

Прохладный. Но вместо eval вы, вероятно, захотите использовать применение:

user=> (apply #(str 1)) 
; ArityException Wrong number of args (1) 

Ups! Это не удалось. apply не перегружать 0-Arity, но мы можем передать пустой список:

user=> (apply #(str 1)()) 
"1" 

намного лучше. Давайте делать это с картой:

user=> (map #(apply %()) [#(str "a" "b") #(str "c" "d") #(str "e" "f")]) 
("ab" "cd" "ef") 

Или еще лучше, учитывая ваши функции не получают никаких аргументов, вы бы лучше сделать, как было предложено @Magos:

user=> (map #(%) [#(str "a" "b") #(str "c" "d") #(str "e" "f")])                    
("ab" "cd" "ef") 
+1

Спасибо за полный и довольно прогрессивный ответ :) – slhsen

5

Вы можете использовать (fn [f] (f)) или #(%) в своем map.

+0

Никогда не думал о функции, которая принимает функцию как аргумент и просто запускает ее. Это довольно круто спасибо :) – slhsen

1

eval оценивает форму Clojure (то есть, список, содержащий «код»). У вас есть функции, а не формы.

Вам map с apply, но apply принимает как функцию и seq аргументов.Так как у вас нет аргументов, вы можете использовать пустой вектор:

(map apply 
    [#(str "a" "b") #(str "c" "d") #(str "e" "f")] 
    (repeat [])) 

Но было бы короче и, возможно, понятнее, если вы используете for:

(for [f [#(str "a" "b") #(str "c" "d") #(str "e" "f")]] 
    (f)) 
+0

«У вас есть функции, а не формы»: это немного вводит в заблуждение, так как функции тоже являются формами; они являются постоянными значениями, такими как числа, и как таковые, 'eval' действует как' identity'. – coredump

+0

Нет, функция - это объект времени выполнения, который способен выполнять некоторые вычисления. Форма представляет собой описание некоторой части кода, которая может или не может быть * описана * вызовом функции. Функция не более форма, чем дельфин, это изображение дельфина. –

+0

'eval' - это не макрос, а функция, которая следует тем же правилам оценки, что и другие функции; '(f x y)' сначала оценивает 'x' и' y', а затем применяет 'f' к соответствующим результатам. '(eval (fn() ...))' сначала оценивает первый аргумент, который возвращает объект функции (IFn), а затем вызывает оценщика по этому результату, который оказывается одним и тем же объектом. Если у вас была функция, указанная как «in» (fn() ...) ', тогда аргумент будет неоценимым выражением, a.k.a. a form, а' eval' создаст объект функции. Разница между «(eval dolphin)» и «(eval 'dolphin)». – coredump

1

Примечание

Как Nathan Davis wrote, eval оценка clojure вид:

(eval '(+ 1 1)) ;=> 2 

Что он делает с функцией? Ничего!. eval рассматривает функцию как литералы:

((eval +) 1 1) ;=> 2 

В вашем случае:

(def stuff [#(str "a" "b") #(str "c" "d") #(str "e" "f")]) 

... мы имеем

(= stuff (map eval stuff)) ;=> true 

Хотя

(= [#(str "a" "b") #(str "c" "d") #(str "e" "f")] 
    (map eval [#(str "a" "b") #(str "c" "d") #(str "e" "f")])) 
;=> false 

... так как соответствующие функции a re различные объекты, хотя и идентичны в работе.

Вот почему eval бесполезно для вас. Итак, следуйте Magos's advice.

0

Может быть, это стоит того, чтобы посмотреть на pcalls и/или pvalues

(pcalls #(str "a" "b") #(str "c" "d") #(str "e" "f")) 
;=> ("ab" "cd" "ef") 

Обратите внимание, что pcalls использует future внутри!