我正在尝试解决我实现自己的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是如何与我在第一个例子中创建的向量不同的类型。
为什么reduce
和apply
在第二个示例中表现不同?
答案 0 :(得分:2)
简单地:
(defn my-comp [& fns]
(fn [x] (reduce #(%2 %1) x fns)))
给
((my-comp rest reverse) [1 2 3 4])
;(4 3 2)
my-comp
为空参数列表返回identity
函数。要绕过(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)
它可以正常工作,因为只有第一个函数应该具有接受许多参数的能力,因为其他函数将应用于先前调用的函数返回的值。