Skip to content

Commit 21d434d

Browse files
committed
[ADT] Add zip_equal for iteratees of equal lengths
Add a new version of `zip` that assumes that all iteratees have equal lengths. The difference compared to `zip_first` is that `zip_equal` checks this assumption in builds with assertions enabled. This will allow us to clearly express the intent when working with equally-sized ranges without having to write this assertion manually. This is similar to Python's `zip(..., equal=True)` [1] or `more_itertools.zip_equal` [2]. I saw this first suggested by @benvanik. [1] https://peps.python.org/pep-0618/ [2] https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.zip_equal Reviewed By: dblaikie Differential Revision: https://reviews.llvm.org/D138865
1 parent aa7a3d4 commit 21d434d

File tree

2 files changed

+45
-1
lines changed

2 files changed

+45
-1
lines changed

llvm/include/llvm/ADT/STLExtras.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -720,12 +720,15 @@ make_early_inc_range(RangeT &&Range) {
720720
EarlyIncIteratorT(std::end(std::forward<RangeT>(Range))));
721721
}
722722

723-
// forward declarations required by zip_shortest/zip_first/zip_longest
723+
// Forward declarations required by zip_shortest/zip_equal/zip_first/zip_longest
724724
template <typename R, typename UnaryPredicate>
725725
bool all_of(R &&range, UnaryPredicate P);
726+
726727
template <typename R, typename UnaryPredicate>
727728
bool any_of(R &&range, UnaryPredicate P);
728729

730+
template <typename T> bool all_equal(std::initializer_list<T> Values);
731+
729732
namespace detail {
730733

731734
using std::declval;
@@ -879,6 +882,20 @@ detail::zippy<detail::zip_shortest, T, U, Args...> zip(T &&t, U &&u,
879882
std::forward<T>(t), std::forward<U>(u), std::forward<Args>(args)...);
880883
}
881884

885+
/// zip iterator that assumes that all iteratees have the same length.
886+
/// In builds with assertions on, this assumption is checked before the
887+
/// iteration starts.
888+
template <typename T, typename U, typename... Args>
889+
detail::zippy<detail::zip_first, T, U, Args...> zip_equal(T &&t, U &&u,
890+
Args &&...args) {
891+
assert(all_equal({std::distance(adl_begin(t), adl_end(t)),
892+
std::distance(adl_begin(u), adl_end(u)),
893+
std::distance(adl_begin(args), adl_end(args))...}) &&
894+
"Iteratees do not have equal length");
895+
return detail::zippy<detail::zip_first, T, U, Args...>(
896+
std::forward<T>(t), std::forward<U>(u), std::forward<Args>(args)...);
897+
}
898+
882899
/// zip iterator that, for the sake of efficiency, assumes the first iteratee to
883900
/// be the shortest. Iteration continues until the end of the first iteratee is
884901
/// reached. In builds with assertions on, we check that the assumption about

llvm/unittests/ADT/IteratorTest.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,33 @@ TEST(ZipIteratorTest, Basic) {
416416
}
417417
}
418418

419+
TEST(ZipIteratorTest, ZipEqualBasic) {
420+
const SmallVector<unsigned, 6> pi = {3, 1, 4, 1, 5, 8};
421+
const SmallVector<bool, 6> vals = {1, 1, 0, 1, 1, 0};
422+
unsigned iters = 0;
423+
424+
for (auto [lhs, rhs] : zip_equal(vals, pi)) {
425+
EXPECT_EQ(lhs, rhs & 0x01);
426+
++iters;
427+
}
428+
429+
EXPECT_EQ(iters, 6u);
430+
}
431+
432+
#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
433+
// Check that an assertion is triggered when ranges passed to `zip_equal` differ
434+
// in length.
435+
TEST(ZipIteratorTest, ZipEqualNotEqual) {
436+
const SmallVector<unsigned, 6> pi = {3, 1, 4, 1, 5, 8};
437+
const SmallVector<bool, 2> vals = {1, 1};
438+
439+
EXPECT_DEATH(zip_equal(pi, vals), "Iteratees do not have equal length");
440+
EXPECT_DEATH(zip_equal(vals, pi), "Iteratees do not have equal length");
441+
EXPECT_DEATH(zip_equal(pi, pi, vals), "Iteratees do not have equal length");
442+
EXPECT_DEATH(zip_equal(vals, vals, pi), "Iteratees do not have equal length");
443+
}
444+
#endif
445+
419446
TEST(ZipIteratorTest, ZipFirstBasic) {
420447
using namespace std;
421448
const SmallVector<unsigned, 6> pi{3, 1, 4, 1, 5, 9};

0 commit comments

Comments
 (0)