在 Clojure 中传递方法名称进行评估的惯用方法?
我正在传递一个函数的名称以在另一个方法中使用。
(defn mapper [m function]
(cond
(= '() m) '()
true (cons (function (first m))
(mapper (rest m) function))))
(println (mapper '((blue red)(green red)(white red)) #'first))
在 Clojure 中是否有更惯用的方法来做到这一点?
I'm passing the name of a function for use in another method.
(defn mapper [m function]
(cond
(= '() m) '()
true (cons (function (first m))
(mapper (rest m) function))))
(println (mapper '((blue red)(green red)(white red)) #'first))
Is there a more idiomatic way to do this in clojure?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
#'first
是名为“first”的 var;first
是名为“first”的 var 的值,即 fn。在这种情况下(#'first foo)
和(first foo)
给出相同的答案,但#'first
每次都会进行额外的取消引用你称之为。因此,除非您希望反复发生取消引用,否则不要这样做。通常不需要使用#'
。map
是惰性的,而你的则不然。内置的map
利用分块 seq 来获得更好的性能,而您的则不然。惯用的代码不必是懒惰的或使用分块的 seq,但请记住,内置函数具有一些这种魔力。所以好好利用一下。(= '() x)
,而是(seq x)
,如果则返回
为空。请注意,在 Clojure 中,nil
>x(= '() nil)
为 false。()
即可。map
首先接受函数参数,因为它接受多个集合参数。当函数接受多个参数时,这些参数必须位于参数列表的最后。我认为另一种读法也更好:“(map f coll):在这个集合中映射这个函数”。cond
。您可以使用if
来代替。如果if
中的某个分支返回nil
,则可以使用when
。在适当的时候使用when
和if
很好,因为它们会立即向读者表明你的意图,而cond
可以做任何事情并强迫读者阅读更多内容。Rafał Dowgird 的版本是惯用的,只是我会颠倒论证的顺序。我会这样称呼它:
#'first
is the var called "first";first
is the value of the var called "first", i.e. the fn. In this case(#'first foo)
and(first foo)
give the same answer, but#'first
does an extra dereference every time you call it. So don't do this unless you want that dereference to happen over and over. There's usually no need to use#'
.map
is lazy, whereas yours isn't. The built-inmap
takes advantage of chunked seqs for better performance, whereas yours doesn't. Idiomatic code doesn't have to be lazy or use chunked seqs, but keep in mind that the builtins have some of this magic going on. So it's good to take advantage.(= '() x)
, the idiomatic test for an empty seq is(seq x)
, which returnsnil
ifx
is empty. Note that in Clojure,(= '() nil)
is false.()
.map
takes the function argument first because it accepts multiple collection arguments. When a function takes multiple arguments, those arguments have to go last in the argument list. I think it reads better the other way too: "(map f coll): map this function across this collection".cond
if you only have two options. You can useif
instead. And if one of the branches in yourif
returnsnil
, you can usewhen
. It's nice to usewhen
andif
when appropriate, because they signal your intentions to the reader immediately, whereascond
could do anything and forces the reader to read more.Rafał Dowgird's version is idiomatic, except I'd flip the order of arguments around. And I'd call it like this:
我相信你已经明白了这句话大部分是惯用的。 Clojure 自己的
map
使用:我已经严重缩短了它 - 原始版本产生一个惰性序列,处理多个集合,分块序列等。顺便说一下 - 我假设你想传递实际的函数,不是名字。
coll 和 f 是分别表示集合和函数的惯用参数名称。
I believe you got it mostly idiomatic. Clojure's own
map
uses:I have shortened it severely - the original produces a lazy sequence, deals with multiple collections, chunked-seqs, etc. By the way - I assume you want to pass the actual function, not it's name.
The
coll
andf
are idiomatic arg names to represent collections and functions, respectively.你的版本对我来说看起来不错。您在 clojure 代码库中看到的常用名称是“coll”,表示集合。我还见过“xs”,我认为这是 Haskell 风格。您还可以参考 Clojure 库编码标准 关于各种约定。
回到这个例子:两个观察结果。
考虑到这两点,如果我重写您的代码:
请注意,就集合而言, conj 做了“正确的事情”。它将新元素添加到列表的头部、向量的尾部等等。另请注意在传统 lisp 中使用“next”而不是第一个/其余习惯用法。 'next' 返回第一个元素之后的元素序列。因此,可以通过对集合进行 seq'ing 来检查空性,对于空列表或空向量将返回 nil。这样它适用于所有集合。
Your version looks good to me. The usual names you will see in the clojure code base is 'coll' for collections. I have also seen 'xs' which is the Haskell style, I think. You may also refer to the Clojure library coding standards on various conventions.
Coming back to the example: Two observations.
With these two in mind, if I rewrite your code:
Note that conj does the "right thing" as far as collections are concerned. It adds the new element to the head of a list, to the tail of a vector and so on. Also note the use of 'next' instead of the first/rest idioms in traditional lisp. 'next' returns a sequence of elements after the first element. So, empty-ness can be checked by seq'ing on the collection which will return nil for an empty list or an empty vector. This way it works for all collections.