简体   繁体   English

Clojure中是否有标准的参数序列同一性函数?

[英]Is there a standard argument sequence identity function in Clojure?

Is there a function in the Clojure standard library equivalent to the following? Clojure标准库中是否有一个等效于以下功能的函数?

(fn [& args] args)

If not, why? 如果没有,为什么?

Example usage: 用法示例:

(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))

Please note that I am not asking for a transformative, eager function such as vector or list . 请注意,我并没有要求使用vectorlist这样的变换性,热切的功能。

There is no such function, you are free to implement and use it as you like: 没有这样的功能,您可以随意实现和使用它:

(defn args [& args] args)

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

Why isn't it already available? 为什么它不可用?

This is rarely fruitful question: not only we don't know what happens in the mind of implementors, it is impracticable to ask them to justify or document why they did not do something. 这是很少见卓有成效的问题:不仅我们不知道在实现者的心态发生了什么,这是行不通的,要求他们证明或文档为什么他们没有做什么。 Was adding this function ever considered? 是否曾考虑添加此功能? How can we know? 我们怎么知道 Is there really a reason, or did it just happen? 真的有原因吗,还是只是发生了?

On the other hand, I agree that args feels simpler because it passes around an already existing immutable sequence. 另一方面,我同意args感觉更简单,因为它绕过了已经存在的不可变序列。 I also can understand if you think that not converting the arguments as a persistent list in the first place is better, if only for the sake of parcimony. 我也可以理解,如果您认为仅出于礼节考虑, 不首先将参数转换为持久性列表会更好。

But this is not how it is implemented, and the overhead of using list is really negligible (and specialized when building from an instance of ArraySeq ). 但是,这不是它是如何实现的,以及使用的开销list (和真的是可以忽略不计的专门从实例建立时ArraySeq )。 You are supposed to code to an interface and never look behind the curtain, and from this point of view, list and args are equivalent , even though they do not return identical results. 您应该对接口进行编码,并且永远不要掩盖,从这个角度来看,即使listargs不会返回相同的结果,它们也是等效的

You added a remark about laziness, and you are right: if you ever need to take the arguments from a variadic function and pass it to a function which operates on sequences, The version with list will consume all the given arguments whereas args will not. 您添加了关于懒惰的评论,这是对的:如果您需要从可变参数函数中获取参数并将其传递给对序列进行操作的函数,带有list的版本将使用所有给定的参数,而args则不会。 In some cases, as with (apply list (range)) , where you literally pass an infinite number of arguments, this might hang forever. 在某些情况下,就像(apply list (range)) ,您在字面上传递了无数个参数,这可能会永远挂起。

From that point of view, the little args function is in fact interesting: you can move from arguments to actual sequences without introducing potential problems. 从这个角度来看,小的args函数实际上很有趣:您可以从参数移到实际序列,而不会引入潜在的问题。 I am however not sure how often this case happens in practice. 但是,我不确定这种情况在实践中多久发生一次。 In fact, I have a hard time finding a use case where laziness in the argument list really matters as far as args is concerned. 实际上,我很难找到一个用例,其中就args而言,参数列表中的惰性确实很重要。 After all, in order to pass an infinite sequence, the only way (?) is to use apply: 毕竟,为了传递无限序列,唯一的方法(?)是使用apply:

(apply f (infinite))

In order to have a use-case for args , that means that we want to convert from an argument list back to a single list so that another function g can use it as a sequence, like so: 为了为args提供用例,这意味着我们要从参数列表转换回单个列表,以便另一个函数g可以将其用作序列,如下所示:

(g (apply args (infinite)))

But in that case, we could directly call: 但是在那种情况下,我们可以直接调用:

(g (infinite))

In your example, g would stand for cons inside lazy-map , but since f is given in input, we cannot write (cons (map ...) ...) directly. 在您的示例中, g代表lazy-map内的cons ,但是由于f是在输入中给出的,因此我们无法直接编写(cons (map ...) ...) The example thus looks like a genuine use case for args , but you should document the function heavily because the snippet you gave is quite complex. 因此,该示例看起来像是args的真实用例,但是您应该大量记录该功能,因为给出的代码段非常复杂。 I tend to think that giving an unbounded number of arguments to a function is a code smell: should every function with an [& args] signature avoid consuming all arguments because the given sequence might in fact be infinite, like lazy-map does? 我倾向于认为给函数提供无限数量的参数是一种代码味道:每个具有[& args]签名的函数都应避免使用所有参数,因为给定的序列实际上可能像lazy-map一样是无限的? I'd rather have a single argument be a lazy sequence for this kind of usage (and pass identity where needed) instead of the whole argument list, to clarify the intent. 我想让一个参数作为这种用法的懒惰序列(并在需要时传递identity ),而不是整个参数列表,以阐明意图。 But in the end, I am not strongly opposed to the use of args either. 但是最后,我也不强烈反对使用args

So to conclude, unless you manage to convince Rich Hickey to add args as a core function, I am confident that almost nobody will want to depend on an external library which does just this 1 : it is unfamiliar, but also trivial to implement and mostly useless. 综上所述,除非您设法说服Rich Hickey将args作为核心函数添加,否则我相信几乎没有人会希望依赖于这样做的外部库1 :它不熟悉,但实现起来却很琐碎,而且大多数情况下无用。 The only reward is knowing that you skip a little transformation step which costs nothing in most cases. 唯一的收获就是知道您跳过了一些转换步骤,在大多数情况下,该步骤不会花费任何费用。 Likewise, do not worry about having to choose between a vector and a list: it has practically no influence on your code and you can still modify the code later if you can prove it is necessary. 同样,不必担心必须在向量和列表之间进行选择:它实际上对代码没有影响,并且如果可以证明有必要的话,您仍然可以稍后修改代码。 Regarding laziness, while I agree that wrapping arguments in lists or vectors can be problematic with unbounded argument lists, I am not sure the problem actually arises in practice. 关于懒惰,尽管我同意将参数包装在列表或向量中对于无限制的参数列表可能会出现问题,但我不确定问题实际上是否会在实际中出现。


1 . 1 Of course, if it ever reaches clojure.core , everybody will be quick to say that is is a fundamental operation which is most useful and definitely idiomatic </cynic> 当然,如果它到达clojure.core ,每个人都会很快地说这是最有用的操作,并且绝对是惯用的</ cynic>

There's identity function. identity功能。 It takes an argument and just returns that argument. 它接受一个参数,然后返回该参数。 Clojure's identity is single arity though. Clojure的identity不过是单身。

usage: 用法:

(identity 4) ;=> 4

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

I don't think there's much sense in having the identity function with variable arity since Clojure functions return only one value. 我认为使用具有可变Arity的identity函数没有多大意义,因为Clojure函数仅返回一个值。 If you want to return multiple values from a function, then you can wrap them in a seq which you can later destructure. 如果要从一个函数返回多个值,则可以将它们包装在一个seq中,以便以后进行分解。 In that case you can have something like this: 在这种情况下,您可以拥有以下内容:

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

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

Hope this helps. 希望这可以帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM