Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么许多Clojure函数是可变的?_Clojure_Variadic Functions - Fatal编程技术网

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

为什么许多Clojure函数是可变的?,clojure,variadic-functions,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 其他示例包括concat和min 作为Clojure新手,我的问题是,为什么这些函数是可变的?我希望他们能按顺序操作。使用apply是获得我想要的东西的最佳/惯用方法吗 注意:我不是想说使用可变函数不好,或者

我在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
是获得我想要的东西的最佳/惯用方法吗


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


编辑:我认为原来的问题不清楚。我的意思是:

在我使用过的其他编程语言中,有一些类似于的操作,例如添加数字、查找较大的元素、连接列表

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

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

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

但是,Clojure添加了第三个用例:

3) 使用可变函数组合0到n个元素

所以问题是,Clojure为什么要添加第三个案例?
保罗的回答表明:

  • 这使得代码更加灵活
  • 历史的力量在起作用
1)方便。使用数学函数,例如
+
,当您只是尝试进行一些计算时,将所有内容按顺序包装会很烦人

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

3) 期望值。这就是这些函数在其他Lisp中的工作方式,同样,在其他函数语言中,它们的语法也有所不同,因此对于来到Clojure的人来说,这是一个合理的期望值。我想说,在其他函数语言中,将函数(如
+
应用于序列)的惯用方法是使用
reduce
foldl
/
foldr
,因此这也与Clojure处理它的方式一致

4) 灵活性。这些函数可以与高阶函数一起使用,例如
map
,如果它们是可变的,则使用起来更方便。假设你有三个向量,你想对同一位置的元素应用一个函数。如果您的函数是可变的,那么您可以将map与多个集合一起使用(map也必须是可变的;):

这比如果所有这些函数都只接受集合的话要方便得多

惯用用法:(根据科塔拉克的精彩评论编辑)

这取决于您是否应该使用
reduce
apply

对于数学函数(
+,-,*,/,等等),
在Java世界中使用两个参数
reduce
更有意义,因为它可以直接使用两个参数的Java版本。使用
apply
时,它们会执行一种隐式的
reduce
(该函数添加两个参数,然后使用结果、下一个参数和其他参数进行递归。reduce的作用非常类似。)

对于
str
而言,使用
apply
可能更有效。当使用多个参数调用
str
时,它将创建一个StringBuilder,向其中添加所有参数,然后创建一个字符串。使用
reduce
将创建StringBuilder n-1次,每次仅添加一个字符串。这和笑话一样,导致了O(n^2)的复杂性


到目前为止的结论是:在数学函数中使用
apply
没有多大伤害,但在
str
中使用
reduce
可能会非常昂贵。

亚历克斯·米勒(Alex Miller)刚刚在他的博客中写到了一个相关问题:

我不同意1和2:这很不方便,或者效率较低,或者对我来说也是一样,因为我从序列开始
+
是一种转移注意力的方法,因为它有时像二进制加法,有时像一个
求和
函数——我在讨论它行为的
求和
部分;我将从OP中删除它。但是对于3和4来说,这是一个很好的例子!1和2:你可以从一个序列开始,但不是每个人都这样做!我想,既然clojure拥有deftype/defrecord和协议,那么这些函数可能会被重载到序列中,我不确定在这之前clojure中是否完全可以做到这一点。如果您没有依赖于类型的分派,并且在运行时查找如何分派(无论是基于数字还是序列),那么您将付出性能代价。我认为没有,至少在clojure.core中没有。但是我认为
(reduce+[yourlist])
和类似的
max
等等实际上可能比自己写的要短,或者至少不会长太多。我想这可能也是它们还不存在的原因。不要从列表等函数中滚动求和。应用是将集合用作函数参数的惯用方法。使用
reduce
还是
Apply
取决于函数。对于
+
来说,它是
reduce
,因为两个参数版本可以内联,而更高的变量版本不能内联。他们在引擎盖下做了一个
减少
。对于
str
它是
apply
,因为
str
可以重新使用底层的
StringBuilder
。因此,它比使用
reduce时效率更高,生成的中间字符串垃圾更少
(map + [1 2 3 4] [2 3 4 5] [3 4 5 6])
; [6 9 12 15]