5

决定 Clojure 核心中参数函数顺序的规则是什么(如果有)?

  • 函数喜欢mapfilter期望数据结构作为最后一个参数。
  • 函数喜欢assocselect-keys期望数据结构作为第一个参数。
  • 函数喜欢mapfilter期望函数作为第一个参数。
  • 像这样update-in的函数期望一个函数作为最后一个参数。

这在使用线程宏时会引起痛苦(我知道我可以使用as->),那么这些决定背后的原因是什么?很高兴知道这样我的功能可以尽可能地符合伟人所写的功能。

4

2 回答 2

8

对集合进行操作的函数(因此获取和返回数据结构,例如conj, merge, assoc, )首先get获取集合。

对序列进行操作的函数(因此采用并返回对数据结构的抽象map,例如)采用最后filter的序列。

更加了解 [集合函数和序列函数之间] 的区别以及这些转换何时发生是学习 Clojure 的更微妙的方面之一。

(亚历克斯米勒,在这个邮件列表线程中

这是智能地使用 Clojure 的序列 API的重要部分。例如,请注意它们在Clojure Cheatsheet中占据了不同的部分。这不是一个小细节。这是如何组织功能以及如何使用它们的核心。

在区分这两种功能时,回顾一下对心理模型的描述可能会很有用:

我通常在 Clojure 中非常清楚我何时使用具体集合或序列。在许多情况下,我发现数据流从集合开始,然后进入序列(由于应用了序列函数),然后有时在静止时回到集合(通过 into、vec 或 set)。转换器对此进行了一些更改,因为它们允许您将目标集合与转换分开,因此通过使用转换器应用到所有时间(如果您愿意)更容易留在集合中。

当我构建或处理集合时,通常构建它的代码是“关闭的”,并且集合类型是已知且显而易见的。通常,顺序数据更有可能是向量,而 conj 就足够了。

当我在“序列”中思考时,我很少做像“添加最后一个”这样的操作——相反,我在思考整个集合的术语。

如果我确实需要做类似的事情,那么我可能会转换回集合(通过 into 或 vec)并再次使用 conj。

Clojure 的常见问题解答有一些很好的经验法则和可视化技术,可以直观地了解 collection/first-arg 与 sequence/last-arg。

于 2018-05-11T07:23:52.130 回答
5

与其让这成为一个仅链接的问题,我将粘贴 Rich Hickey 对 Usenet 问题“Argument order rules of thumb”的回复的引用:

考虑序列的一种方法是从左侧读取它们,并从右侧馈送:

<- [1 2 3 4]

大多数序列函数消耗和产生序列。因此,将其可视化的一种方法是链:

地图<-过滤器<-[1 2 3 4]

考虑许多 seq 函数的一种方法是它们以某种方式参数化:

(地图 f)<-(过滤器预测)<-[1 2 3 4]

因此,序列函数最后获取它们的源,以及它们之前的任何其他参数,并且部分允许如上所述的直接参数化。在函数式语言和 Lisp 中有这样的传统。

请注意,这与最后获取主操作数不同。一些序列函数有多个源(concat、interleave)。当序列函数是可变参数时,它通常在它们的源中。

我不认为变量 arg 列表应该是主要操作数去向的标准。是的,它们必须排在最后,但正如 assoc/dissoc 的演变所示,有时会在后面添加可变参数。

同上。每个库最终都会得到一个更加独立于顺序的部分绑定方法。对于 Clojure,它是 #()。

那么一般规则是什么?

主要集合操作数排在第一位。这样可以写 -> 及其同类,它们的位置与它们是否具有可变参数无关。在 OO 语言和 CL 中有这样的传统(CL 的 slot-value、aref、elt - 事实上,在 CL 中最常让我绊倒的是 gethash,这与那些不一致)。

所以,最终有两条规则,但它不是一个免费的。序列函数最后获取它们的源,而集合函数首先获取它们的主要操作数(集合)。并不是说这里和那里没有一些我需要解决的问题(例如设置/选择)。

我希望这有助于使它看起来不那么虚假,

富有的

现在,如何区分“序列函数”和“集合函数”对我来说并不明显。也许其他人可以解释这一点。

于 2018-05-10T17:00:12.427 回答