为什么许多Clojure函数都是可变的?

时间:2011-10-19 15:10:38

标签: clojure variadic-functions

这是我在Clojure中遇到的一个问题:

user=> (max [3 4 5 6 7])
[3 4 5 6 7] ; expected '7'

有些功能不符合我的预期!
这是使用apply的一个解决方案:

user=> (apply max [3 4 5 6 7])
7

其他示例包括concatmin 我的问题,作为一个Clojure新手,为什么这些函数是可变的?我希望他们对序列进行操作。使用apply是获得我想要的最佳/惯用方法吗?


注意:我并不是说拥有可变函数或者有更好的方法是不好的。我只是想知道是否遵循规则或惯例,或者我应该注意这种方法是否具有特定优势。


编辑:我认为最初的问题不明确。这就是我的意思:

在我使用的其他编程语言中,有monoid - 类似的操作,例如添加数字,查找更大的元素,连接列表。

这些操作通常有两个用例:

1)使用接受两个参数的函数组合两个元素 2)使用接受元素列表(或序列)的函数

组合0到n个元素

第二种情况的函数可以从第一种情况的函数构建(通常使用reduce)。

然而,Clojure增加了第三个用例:

3)使用可变函数

组合0到n个元素

所以问题是,为什么Clojure会添加第三种情况? 保罗的回答表明:

  • 这允许更灵活的代码
  • 有工作中的历史力量

2 个答案:

答案 0 :(得分:15)

1)方便。使用+之类的数学函数,当您尝试进行某些计算时,将所有内容包装在序列中会很烦人。

2)效率。使用集合/序列包装所有内容也会效率低下,首先需要创建序列,然后需要在运行时解压缩而不是在编译时查找正确的Java函数时间。

3)期望。这就是这些函数在其他Lisp中的工作方式,并且在其他函数式语言中使用的语法略有不同,因此对于来Clojure的人来说是合理的期望。我想说其他函数式语言中将+这样的函数应用于序列的惯用方法是使用reducefoldl / foldr,所以这个也符合Clojure的处理方式。

4)灵活性。这些函数可以与更高阶函数一起使用,例如map,这使得它们在可变参数时更方便使用。假设您有三个向量,并且您希望将函数应用于同一位置的元素。如果你的函数是可变参数,那么你可以使用带有多个集合的map(map也必须是variadic然后;)):

(map + [1 2 3 4] [2 3 4 5] [3 4 5 6])
; [6 9 12 15]

如果所有这些功能都只是采集集合,那么这比你所拥有的方便得多。

惯用法:(在kotarak的优秀评论后编辑)

如果您使用reduceapply,则取决于功能。

对于数学函数(+,-,*,/,etc.),它在Java世界reduce中取2个参数更有意义,因为它可以直接使用2参数Java版本。使用apply它们可以做一个隐式reduce(函数添加两个参数,然后重复结果,下一个参数和其余参数。这几乎就是reduce的作用。)

使用str的{​​{1}}可能更有效率。当使用多个参数调用apply时,它会创建一个StringBuilder,向其添加所有参数,然后创建一个字符串。使用str StringBuilder将创建n-1次,每次只添加一个字符串。这与Shlemiel the painter笑话一样,导致O(n ^ 2)复杂度。

到目前为止的判决:将reduce与数学函数一起使用不会造成太大影响,但将applyreduce一起使用可能会非常昂贵。

答案 1 :(得分:3)

Alex Miller刚刚在他的博客中写到了一个相关问题:2 is a smell