【问题标题】:How to evaluate sequence of pure functions in Clojure如何在 Clojure 中评估纯函数序列
【发布时间】:2015-08-10 21:16:11
【问题描述】:

我的问题与这个问题非常相似:How to evaluate a sequence of impure functions in Clojure? 但不是不纯函数,我如何评估纯函数序列并将结果作为另一个序列?

假设我们有一个函数向量,例如:

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

我们需要这样的输出:

("ab" "cd" "ef")

我尝试过类似的方法:

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

但它只返回一个函数引用向量。

【问题讨论】:

    标签: clojure lisp


    【解决方案1】:

    有多种方法可以满足您的要求。这取决于你是否想要一个 惰性序列(你可能想要惰性,因为没有副作用,但如果你想要缓存密集的计算,你可能想要非惰性),或者如果您想要一个向量作为输出(与您的输入相同)。我会尽力按照你的想法去做。

    您的 eval 地图对每个 fn 执行以下操作:

    user=> (eval #(str 1))
    #<user$eval1332$fn__1333 user$eval1332$fn__1333@38747597>
    

    但是你想要类似下面的东西:

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

    您希望 eval 应用 fn,即:fn 应该是列表的第一个元素。让我们把它放在一个列表中:

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

    酷。但您可能想使用 apply 来代替 eval:

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

    起来!它失败了。 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")
    

    【讨论】:

    • 感谢您提供完整且相当进步的答案:)
    【解决方案2】:

    您可以在您的map 中使用(fn [f] (f))#(%)

    【讨论】:

    • 从来没有想过一个函数可以将函数作为参数并运行它。这很酷谢谢:)
    【解决方案3】:

    eval 计算 Clojure 表单(即包含“代码”的列表)。你有函数,而不是表单。

    mapapply,但 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))
    

    【讨论】:

    • “你有函数,而不是表单”:这有点误导,因为函数也是表单;它们是像数字一样的常量值,因此,eval 的行为类似于 identity
    • 不,函数是能够执行某些计算的运行时对象。表单是对某段代码的描述,它可能描述函数调用。函数只是一种形式,就像海豚是海豚的图片一样。
    • eval 不是宏,而是遵循与其他函数相同的评估规则的函数; (f x y) 首先评估 xy,然后将 f 应用于各自的结果。 (eval (fn () ...)) 首先计算第一个参数,它返回一个函数对象 (IFn),然后对该结果调用评估器,结果证明是同一个对象。如果你引用了函数,如'(fn () ...),那么参数将是一个未计算的表达式,也就是一个表单,eval 将创建函数对象。区别在于(eval dolphin)(eval 'dolphin)
    【解决方案4】:

    注意

    作为Nathan Davis wroteeval 计算一个clojure form

    (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
    

    ...因为相应的功能是不同的对象,但在操作上是相同的。

    这就是为什么eval 对你没用。所以请关注Magos's advice

    【讨论】:

      【解决方案5】:

      也许值得看看pcalls 和/或pvalues

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

      请注意pcalls 在里面使用future

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-11-14
        • 2011-10-21
        • 1970-01-01
        • 2012-11-19
        • 1970-01-01
        • 2020-08-24
        • 1970-01-01
        相关资源
        最近更新 更多