3

Clojure 标准库中是否有与以下等效的函数?

(fn [& args] args)

如果不是,为什么?

示例用法:

(take 10 (apply (fn [& args] args) (range)))
;=> (0 1 2 3 4 5 6 7 8 9)

;; ironically, map isn't lazy enough, so let's take it up to 11
(defn lazy-map [f & colls]
  (lazy-seq (cons (apply f (map first colls))
                  (apply lazy-map f (map rest colls)))))

(defn transpose [m]
  (apply lazy-map (fn [& args] args) m))

(defn take-2d [rows cols coll]
  (take rows (map (partial take cols) coll)))

(take-2d 3 3 (transpose (map (partial iterate inc) (range))))
;=> ((0 1 2) (1 2 3) (2 3 4))

请注意,我并不是要一个变革性的、急切的功能,例如vectoror list

4

2 回答 2

3

没有这样的功能,你可以随意实现和使用:

(defn args [& args] args)

(set (map type (apply map args [[1 2 3][4 5 6][7 8 9]])))
=> #{clojure.lang.ArraySeq}

为什么它已经不可用了?

这是一个很少有成果的问题:不仅我们不知道实施者的想法,而且要求他们证明或记录他们为什么不做某事是不切实际。是否考虑过添加此功能?我们怎么知道?真的有原因,还是只是发生了?

另一方面,我同意这args感觉更简单,因为它绕过了一个已经存在的不可变序列。如果您认为首先将参数转换为持久列表更好,我也可以理解,即使只是为了节省。

但这不是它的实现方式,使用的开销list真的可以忽略不计(并且在从 的实例构建时专门ArraySeq化)。您应该对界面进行编码,并且永远不要看幕后,从这个角度来看,list它们args等效的,即使它们不返回相同的结果。

您添加了关于惰性的评论,您是对的:如果您需要从可变参数函数中获取参数并将其传递给对序列进行操作的函数,带有的版本list将消耗所有给定的参数,而args不会。在某些情况下,如(apply list (range)),您实际上传递了无限数量的参数,这可能会永远挂起。

从这个角度来看,这个小args函数实际上很有趣:您可以从参数转移到实际序列而不会引入潜在问题。但是,我不确定这种情况在实践中发生的频率。事实上,我很难找到一个用例,其中参数列表中的惰性确实很重要args。毕竟,为了传递一个无限序列,唯一的方法 (?) 是使用 apply:

(apply f (infinite))

为了有 for 的用例args,这意味着我们希望将参数列表转换回单个列表,以便另一个函数g可以将其用作序列,如下所示:

(g (apply args (infinite)))

但在这种情况下,我们可以直接调用:

(g (infinite))

在您的示例中,g将代表consinside lazy-map,但由于f在输入中给出,我们不能(cons (map ...) ...)直接写。因此,该示例看起来像是一个真正的用例args,但您应该大量记录该函数,因为您提供的代码段非常复杂。我倾向于认为给函数提供无限数量的参数是一种代码味道:每个带有[& args]签名的函数是否应该避免使用所有参数,因为给定的序列实际上可能是无限的,就像lazy-map这样?我宁愿让一个参数成为这种用法的惰性序列(并identity在需要的地方传递)而不是整个参数列表,以阐明意图。但最后,我并不强烈反对使用args其中任何一个。

总而言之,除非您设法说服 Rich Hickey 将其添加args为核心功能,否则我相信几乎没有人会想要依赖仅执行此操作的外部库1:它不熟悉,但实现起来也很简单,而且几乎没用. 唯一的回报是知道您跳过了一个在大多数情况下不需要花费任何成本的小转换步骤。同样,不必担心必须在向量和列表之间进行选择:它实际上对您的代码没有影响,如果您能证明有必要,您仍然可以稍后修改代码。关于惰性,虽然我同意将参数包装在列表或向量中可能会给无界参数列表带来问题,但我不确定该问题在实践中是否真的出现。


1 . 当然,如果它达到clojure.core,每个人都会很快说这是一个最有用且绝对惯用的基本操作</cynic>

于 2015-11-24T10:43:12.983 回答
1

identity功能。它接受一个参数并返回该参数。虽然Clojureidentity是单一的。

用法:

(identity 4) ;=> 4

(identity [1 2 3 4]) ;=> [1 2 3 4]

我认为具有可变数量的标识函数没有多大意义,因为 Clojure 函数只返回一个值。如果要从函数返回多个值,则可以将它们包装在 seq 中,以后可以对其进行解构。在这种情况下,你可以有这样的东西:

(defn varity-identity [& args]
    (map identity args))

(varity-identity 1 2 3 4 5) ;=> (1 2 3 4 5)

希望这可以帮助。

于 2015-11-23T22:54:42.247 回答