对变量参数类型的误解

时间:2014-09-02 17:06:56

标签: clojure

我正在尝试解决我实现自己的comp函数的clojure问题。

我有以下表达方式,我的期望是:

(reduce #(apply %2 [%1]) [1 2 3 4] [rest reverse])

这给出了

的输出
(4 3 2)

我尝试将其抽象为这样的函数:

(((fn [& funcs]
        (fn [& args]
       (reduce #(apply %2 [%1]) args funcs)
      )) rest reverse) [1 2 3 4])

但是当我运行它时出现以下错误:

  

CompilerException java.lang.ClassCastException:clojure.lang.ArraySeq   无法强制转换为java.lang.Number,   编译:(/用户/ paulcowan /项目/刮伤/ SRC /刮伤/ core.clj:1:1)

对我来说,我能看到的唯一区别是funcs和args是如何与我在第一个例子中创建的向量不同的类型。

为什么reduceapply在第二个示例中表现不同?

2 个答案:

答案 0 :(得分:2)

简单地:

(defn my-comp [& fns]
  (fn [x] (reduce #(%2 %1) x fns)))

((my-comp rest reverse) [1 2 3 4])
;(4 3 2)
  1. 正如所料,my-comp为空参数列表返回identity函数。
  2. 但它期望所有功能,包括第一次应用, 采取一个论点。
  3. 要绕过(2),请按如下方式进行调整:

    (defn my-comp [& fns]
      (if (empty? fns)
        identity
        (let [[f & fs] fns]
          (fn [& args] (reduce #(%2 %1) (apply f args) fs)))))
    

    除了(1)之外,这只是改写Mark's answer


    为了好玩......

    我们可以用the standard comp

    来定义my-comp
    (defn my-comp [& fns] (apply comp (reverse fns)))
    

    但是反过来可能更有意义,因为comp必须反转其参数列表:

    (defn comp [& fns] (apply my-comp (reverse fns)))
    

    我们甚至可以定义一个参数反转器

    (defn rev-args [f] (fn [& args] (apply f (reverse args))))
    

    ...将一个函数转换成一个与反向参数列表相同的函数。

    然后

    (def comp (rev-args my-comp))
    

    反之亦然:

    (def my-comp (rev-args comp))
    

答案 1 :(得分:1)

首先,我没有收到任何错误消息(也没有正确的结果):

user> (((fn [& funcs]
          (fn [& args]
            (reduce #(apply %2 [%1]) args funcs))) rest reverse) [1 2 3 4])
;; => ()

两个示例之间的区别在于,在第一个示例中,您将值[1 2 3 4]传递给reduce,而在第二个示例中,您传递[[1 2 3 4]](因为args是将函数的所有参数保持为一个向量。

这将有效:

user> (((fn [& funcs]
          (fn [args]
            (reduce #(apply %2 [%1]) args funcs))) rest reverse) [1 2 3 4])
;; => (4 3 2)

但是,要获得能够接受任意数量参数的函数式函数,你应该写这样的东西:

user> (defn my-comp [& fncs]
        (fn [& args]
          (reduce #(%2 %1) ; you can omit apply here, as %2 is already function
                           ; and %1 is always one value, as noisesmith noticed
                  (apply (first fncs) args)
                  (rest fncs))))
;; => #'user/my-comp
user> (def my-fnc (my-comp rest reverse))
;; => #'user/my-fnc
user> (my-fnc [1 2 3 4])
;; => (4 3 2)

它可以正常工作,因为只有第一个函数应该具有接受许多参数的能力,因为其他函数将应用于先前调用的函数返回的值。