Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add allequal #43353

Closed
CameronBieganek opened this issue Dec 6, 2021 · 8 comments · Fixed by #43354
Closed

Add allequal #43353

CameronBieganek opened this issue Dec 6, 2021 · 8 comments · Fixed by #43354

Comments

@CameronBieganek
Copy link
Contributor

Since we already have allunique, it might make sense to also add allequal. One reason to add allequal is that a proper implementation is much faster than the naive length(unique(itr)) == 1.

function allequal(x)
    first, rest = Iterators.peel(x)

    for v in rest
        if v == first
            continue
        else
            return false
        end
    end

    true
end

slow_allequal(x) = length(unique(x)) == 1
julia> using BenchmarkTools

julia> x = randn(1000); y = fill(1, 1000);

julia> @btime allequal($x);
  3.288 ns (0 allocations: 0 bytes)

julia> @btime slow_allequal($x);
  36.791 μs (27 allocations: 65.95 KiB)

julia> @btime allequal($y);
  475.082 ns (0 allocations: 0 bytes)

julia> @btime slow_allequal($y);
  3.816 μs (6 allocations: 608 bytes)
@oscardssmith
Copy link
Member

I think the idiom to use isn't length(unique(x)), but all(isequal(first(x)), x) which is roughly 10% faster than the version you proposed (not sure why).

@CameronBieganek
Copy link
Contributor Author

Interesting! I wonder why that's faster...

I suspect that beginners are more likely to try length(unique(x)) == 1 than all(isequal(first(x)), x). Coming from R, length(unique(x)) == 1 was the first thing that I reached for.

@oscardssmith
Copy link
Member

That's fair, I personally don't like this getting it's own name, but make a PR and triage can decide.

@CameronBieganek
Copy link
Contributor Author

Yeah, I wouldn't necessarily argue for it getting its own name, but since we already have allunique, it seems sensible to also have allequal.

@goretkin
Copy link
Contributor

goretkin commented Dec 7, 2021

We would want allequal([]) == true, the same way all([]) [1]. None of the two idioms mentioned have that behavior, but it is easier to do with length(unique(x)) <= 1

It would be nice if there was an all_transitive (this is not a good name) method that could work for any transitive relation, then allequal(x) could be defined in terms of a more general all_transitive(isequal, x).

[1] imagine a definition like all(Base.splat(isequal), all_pairs(x))

@CameronBieganek
Copy link
Contributor Author

We would want allequal([]) == true, the same way all([])

Yeah, I just did the simple thing in my PR and made it

allequal(itr) = isempty(itr) ? true : all(isequal(first(itr)), itr)

@GregPlowman
Copy link
Contributor

Like some other functions, e.g. all, findall, I sometimes want the predicate version of allequal:

allequal(f, itr) = isempty(itr) ? true : all(x -> isequal(f(x), f(first(itr))), itr)

@CameronBieganek
Copy link
Contributor Author

@GregPlowman That might be nice. Want to open a new issue for that?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants