Check two iterators for equality without collecting

I am looking for simple syntax that would compare two iterators itr1, itr2 for elementwise equality without collecting, and break early if there is a difference. MWE:

julia> VERSION
v"0.6.0-rc1.0"

julia> all(1:3 .== [1,2,3])
true

julia> all(1:3 .== (i for i in 1:3)) # but I want this to be true
false

Note that I know how to implement it, I am looking for simple syntax, equivalent to

function itreq(itr1, itr2)
    for (i1,i2) in zip(itr1, itr2)
        i1 == i2 || return false
    end
    true
end

itreq(1:3, (i for i in 1:3))

EDIT: I realized I really want something equivalent to collect(itr1) == collect(itr2), but without collect. Ie different lengths should also return false, so all(... .== ...) is not good.

In case you are wondering, this is just out of curiosity, the code in question has no performance impact. I am validating that a list of Cartesian indexes are really contiguous and in the right order, with a function not unlike

function combined_size(indexes)
    siz = reduce(max, indexes)
    ran = CartesianRange(siz)
    # FIXME inelegant collect below
    @argcheck collect(indexes) == collect(ran) "Non-contiguous indexes."
    siz
end

We should probably have methods for all and any that work like map, taking a predicate which takes as many arguments as you pass iterables. Could you open a feature request issue?

6 Likes

I thought about it and I am not sure it is a good idea. Something like

all(f, itr...) = all(tup->f(tup...), zip(itr...))

would work, apart from the fact that it would conflict with all(f::Function, A::AbstractArray, region) when used with two iterables, however

all(==, 1:3, (i for i in 1:4))

would be true, which is not necessarily what I want. One would need to test for one sequence consumed earlier than the other, but mapreduce would just finish silently.

Typically map etc error out if the sizes aren’t compatible, and so should an iteration version of mapreduce.

1 Like

How about defining:
iterequal(iter,iters...) = all(t->length(t)==length(iter),iters) && all(all(t->t==x[1],x[2]) for x in zip(iter,zip(iters...)))

which uses all which is short-circuiting on boolean predicates.

In this examples:
julia> iterequal(1:3,1:3) true

and
julia> iterequal(1:3,1:4) false

Only zip ignores length differences, stopping as soon as the first argument stops, and there’s an open issue somewhere with a lot of support for changing that (including from me).

https://github.com/JuliaLang/julia/issues/20499

1 Like