Clojure 中的部分源代码说明
Partial source code clarification in Clojure
我是 clojure 的初学者,目前正在尝试从基础层面理解它。
我一直在试验 partial 以及它如何创建闭包,为了更深入地了解我认为我应该通过执行 (source partial)
来查看源代码。
我明白了
(defn partial
"Takes a function f and fewer than the normal arguments to f, and
returns a fn that takes a variable number of additional args. When
called, the returned function calls f with args + additional args."
{:added "1.0"
:static true}
([f] f)
([f arg1]
(fn
([] (f arg1))
([x] (f arg1 x))
([x y] (f arg1 x y))
([x y z] (f arg1 x y z))
([x y z & args] (apply f arg1 x y z args))))
([f arg1 arg2]
(fn
([] (f arg1 arg2))
([x] (f arg1 arg2 x))
([x y] (f arg1 arg2 x y))
([x y z] (f arg1 arg2 x y z))
([x y z & args] (apply f arg1 arg2 x y z args))))
([f arg1 arg2 arg3]
(fn
([] (f arg1 arg2 arg3))
([x] (f arg1 arg2 arg3 x))
([x y] (f arg1 arg2 arg3 x y))
([x y z] (f arg1 arg2 arg3 x y z))
([x y z & args] (apply f arg1 arg2 arg3 x y z args))))
([f arg1 arg2 arg3 & more]
(fn [& args] (apply f arg1 arg2 arg3 (concat more args)))))
我发现整个定义是多余的,因为我只会以 "varargs" 的方式来写它,即最后两行。
这是一个可读性特征还是我在这里遗漏了一些基本的东西?
这不是可读性问题,而是性能问题。 Stuart Sierra 在 Clojure Don’ts: Optional Arguments with Varargs:
中对此进行了解释
Variable-arity function calls have to allocate a sequence to hold the arguments, then go through apply. Timothy Baldridge did a quick performance comparison showing that calls to a function with multiple, fixed arities can be much faster than variable-arity (varargs) function calls.
在该基准测试中,具有 1 个参数的可变参数版本比具有 1 个参数的多参数版本慢大约一个数量级,而具有 3 个参数的差异高达约 2 个数量级。
这并不是说根本不应该使用可变参数:使用可变参数稀疏地调用 fn 可能不会影响性能,但在从紧密循环调用时可能会严重打击您。
I find the whole definition to be redundant as I would only write it
in a "varargs" fashion,
你说的很对。除了 varargs 定义之外的所有定义都是多余的。正如 指出的那样,其他的是为了提高性能。你会在 clojure 核心中找到很多这样的东西:比如 map
、max
和 comp
。
我是 clojure 的初学者,目前正在尝试从基础层面理解它。
我一直在试验 partial 以及它如何创建闭包,为了更深入地了解我认为我应该通过执行 (source partial)
来查看源代码。
我明白了
(defn partial
"Takes a function f and fewer than the normal arguments to f, and
returns a fn that takes a variable number of additional args. When
called, the returned function calls f with args + additional args."
{:added "1.0"
:static true}
([f] f)
([f arg1]
(fn
([] (f arg1))
([x] (f arg1 x))
([x y] (f arg1 x y))
([x y z] (f arg1 x y z))
([x y z & args] (apply f arg1 x y z args))))
([f arg1 arg2]
(fn
([] (f arg1 arg2))
([x] (f arg1 arg2 x))
([x y] (f arg1 arg2 x y))
([x y z] (f arg1 arg2 x y z))
([x y z & args] (apply f arg1 arg2 x y z args))))
([f arg1 arg2 arg3]
(fn
([] (f arg1 arg2 arg3))
([x] (f arg1 arg2 arg3 x))
([x y] (f arg1 arg2 arg3 x y))
([x y z] (f arg1 arg2 arg3 x y z))
([x y z & args] (apply f arg1 arg2 arg3 x y z args))))
([f arg1 arg2 arg3 & more]
(fn [& args] (apply f arg1 arg2 arg3 (concat more args)))))
我发现整个定义是多余的,因为我只会以 "varargs" 的方式来写它,即最后两行。
这是一个可读性特征还是我在这里遗漏了一些基本的东西?
这不是可读性问题,而是性能问题。 Stuart Sierra 在 Clojure Don’ts: Optional Arguments with Varargs:
中对此进行了解释Variable-arity function calls have to allocate a sequence to hold the arguments, then go through apply. Timothy Baldridge did a quick performance comparison showing that calls to a function with multiple, fixed arities can be much faster than variable-arity (varargs) function calls.
在该基准测试中,具有 1 个参数的可变参数版本比具有 1 个参数的多参数版本慢大约一个数量级,而具有 3 个参数的差异高达约 2 个数量级。
这并不是说根本不应该使用可变参数:使用可变参数稀疏地调用 fn 可能不会影响性能,但在从紧密循环调用时可能会严重打击您。
I find the whole definition to be redundant as I would only write it in a "varargs" fashion,
你说的很对。除了 varargs 定义之外的所有定义都是多余的。正如 map
、max
和 comp
。