我能想到的最简短的测试数组 arr
中所有元素是否相等的方法是 all(arr[1] .== arr)
。虽然这样做很短,但似乎有点不够优雅。是否有内置函数可以完成此操作?
我猜想应该有类似于 ==(arr...)
的函数,但这样做行不通,因为 ==
运算符只能接受两个参数。我不确定 Julia 如何解析诸如 arr[1] == arr[2] == arr[3]
的表达式,但是否有一种方法可以将其适配到具有任意数量元素的数组上呢?
all
是正确的解决方案,但如果你想要针对谓词 p
和可迭代对象 itr
使用方法 all(p, itr)
,因为它将使用短路行为(一旦发现一个 false
就停止)。所以:
all(y->y==x[1], x)
要看到差别,您可以运行以下小速度测试:
for n = 100000:250000:1100000
x = rand(1:2, n);
@time all(x .== x[1]);
@time all(y->y==x[1], x);
println("------------------------")
end
忽略第一次迭代,因为它是编译时间。
0.000177 seconds (22 allocations: 17.266 KiB)
0.006155 seconds (976 allocations: 55.062 KiB)
------------------------
0.000531 seconds (23 allocations: 47.719 KiB)
0.000003 seconds (1 allocation: 16 bytes)
------------------------
0.000872 seconds (23 allocations: 78.219 KiB)
0.000001 seconds (1 allocation: 16 bytes)
------------------------
0.001210 seconds (23 allocations: 108.781 KiB)
0.000001 seconds (1 allocation: 16 bytes)
------------------------
0.001538 seconds (23 allocations: 139.281 KiB)
0.000002 seconds (1 allocation: 16 bytes)
第一个解决方案明显是O(n),而第二个方案最好是O(1),最坏情况下是O(n)(取决于itr
的数据生成过程)。
allsame(x) = all(y->y==x[1],x)
。这门语言的好处之一就是你可以很容易地编写这些快捷方式,并将它们放在自己的核心包中。更普遍地说,Julia社区强烈推崇保持Base
尽可能简洁。每种操作类型的良好、简单的函数名称可以在包中实现,如果足够多的人喜欢它,那么该包就会变得流行,等等...但是Base
仍然保持精简和快速。 - Colin T Bowersallsame([]) == true
,这是应该的,因为空数组内没有不同的元素。 - mschauer很棒的问题@tparker和很好的答案@ColinTBowers。在尝试思考它们两个时,我想到了尝试直截了当的老派Julian方式-for
-loop。结果在一长串相同元素的重要输入上更快,因此我添加了这个注释。另外,函数名称allequal
似乎足够恰当,值得一提。所以这里是不同的变体:
allequal_1(x) = all(y->y==x[1],x)
# allequal_2(x) used to be erroneously defined as foldl(==,x)
@inline function allequal_3(x)
length(x) < 2 && return true
e1 = x[1]
i = 2
@inbounds for i=2:length(x)
x[i] == e1 || return false
end
return true
end
还有基准测试:
julia> using BenchmarkTools
julia> v = fill(1,10_000_000); # long vector of 1s
julia> allequal_1(v)
true
julia> allequal_3(v)
true
julia> @btime allequal_1($v);
9.573 ms (1 allocation: 16 bytes)
julia> @btime allequal_3($v);
6.853 ms (0 allocations: 0 bytes)
更新:另一个重要的基准测试案例是当存在短路机会时。因此(如评论中请求):
julia> v[100] = 2
2
julia> allequal_1(v),allequal_2(v),allequal_3(v)
(false, false, false)
julia> @btime allequal_1($v);
108.946 ns (1 allocation: 16 bytes)
julia> @btime allequal_3($v);
68.221 ns (0 allocations: 0 bytes)
所有事情都一样的情况下,for
版本应该在基础版本中变为allequal
。
reduce.jl
。我希望allequal
(虽然它实际上不应该在Base中)能够像这样,基于一些通用结构(如all
)构建,而不是制作一些超优化的自定义循环。这样,改进all
的性能将改进依赖它的每个函数的性能。 - DNFfoldl
(和其他缩减操作),基于 op 的 零元素。使用另一种带有零元素定义的 foldl
签名,或者只是针对常见二进制操作的专用签名。 - Dan Getzallequal_2
的最优性并不重要,因为它是不正确的!例如,allequal_2([0,0,0])
的计算结果为false
。 - banbhtrue == 1
和 false == 0
的C语言起源有些可疑,所以我混淆了。 - Dan Getz仅有轻微的改进:allsame(x) = all(y -> y == first(x), x)
比 allsame(x) = all(y -> y == x[1], x)
更通用,即使当x
不是一个AbstractArray
时仍然有效,例如生成器。
==(first(x))
而不是y -> y == first(x)
。这个是新的。 - DNFc = [x^2 for x in 1:100]
这样的推导式会分配一个包含前100个整数平方的向量。相比之下,生成器g = (x^2 for x in 1:100)
会按顺序生成相同的值,而不会分配向量。您可以执行类似for s in g; $show s; end
的操作来打印它们。生成器被证明是非常强大的性能工具。 - Pablo San-Joseallequal(arr)
length(unique(arr)) == 1
allequal
; 参见https://docs.julialang.org/en/v1/base/collections/#Base.allequal - Jeff