这是我在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
其他示例包括concat
和min
我的问题,作为一个Clojure新手,为什么这些函数是可变的?我希望他们对序列进行操作。使用apply
是获得我想要的最佳/惯用方法吗?
注意:我并不是说拥有可变函数或者有更好的方法是不好的。我只是想知道是否遵循规则或惯例,或者我应该注意这种方法是否具有特定优势。
编辑:我认为最初的问题不明确。这就是我的意思:
在我使用的其他编程语言中,有monoid - 类似的操作,例如添加数字,查找更大的元素,连接列表。
这些操作通常有两个用例:
1)使用接受两个参数的函数组合两个元素 2)使用接受元素列表(或序列)的函数
组合0到n个元素第二种情况的函数可以从第一种情况的函数构建(通常使用reduce
)。
然而,Clojure增加了第三个用例:
3)使用可变函数
组合0到n个元素所以问题是,为什么Clojure会添加第三种情况? 保罗的回答表明:
答案 0 :(得分:15)
1)方便。使用+
之类的数学函数,当您尝试进行某些计算时,将所有内容包装在序列中会很烦人。
2)效率。使用集合/序列包装所有内容也会效率低下,首先需要创建序列,然后需要在运行时解压缩而不是在编译时查找正确的Java函数时间。
3)期望。这就是这些函数在其他Lisp中的工作方式,并且在其他函数式语言中使用的语法略有不同,因此对于来Clojure的人来说是合理的期望。我想说其他函数式语言中将+
这样的函数应用于序列的惯用方法是使用reduce
或foldl
/ 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的优秀评论后编辑)
如果您使用reduce
或apply
,则取决于功能。
对于数学函数(+,-,*,/,etc.)
,它在Java世界reduce
中取2个参数更有意义,因为它可以直接使用2参数Java版本。使用apply
它们可以做一个隐式reduce
(函数添加两个参数,然后重复结果,下一个参数和其余参数。这几乎就是reduce的作用。)
使用str
的{{1}}可能更有效率。当使用多个参数调用apply
时,它会创建一个StringBuilder,向其添加所有参数,然后创建一个字符串。使用str
StringBuilder将创建n-1次,每次只添加一个字符串。这与Shlemiel the painter笑话一样,导致O(n ^ 2)复杂度。
到目前为止的判决:将reduce
与数学函数一起使用不会造成太大影响,但将apply
与reduce
一起使用可能会非常昂贵。
答案 1 :(得分:3)
Alex Miller刚刚在他的博客中写到了一个相关问题:2 is a smell