Search code examples
c++haskellfunctional-programmingtransposerange-v3

How to emulate Haskell's Data.List.transpose in C++ with Range-v3?


These are some inputs and relative desired outputs

std::vector<std::string> v1{"aeiou", "bcdfghjk"};
std::vector<std::string> v2{"aeiou", "bcd"};
auto w1 = v1 | wanne_be_transpose;
auto w2 = v2 | wanne_be_transpose;
// w1 = {"ab","ec","id","of","ug","h","j","k"}
// w2 = {"ab","ec","id","o","u"}

Honestly I have no idea how to emulate it with ranges. (Even less in the general case of v1.size() > 2, but my use case is inherently v1.size() == 2, so I'd be ok with a solution only for this case.)

Initially I thought I could ranges::views::zip together and then convert the pairs to inner ranges, but that wouldn't work, because zip's output is as long as the shortest sequence.

Well, in Haskell there's Data.List.transpose:

λ> Data.List.transpose $ ["aeiou","bcdfghjk"]
["ab","ec","id","of","ug","h","j","k"]
λ> Data.List.transpose $ ["aeiou","bcd"]
["ab","ec","id","o","u"]

Coming back to this question after some time, I think I can say that ranges::views::zip or ranges::views::zip_with couldn't be of help in implementing a general transpose operation, because they take a variadiac number of arguments (so a number known at compile time), whereas transpose would have to support a number of ranges know only at run time. The reason is explained in all 3 answers to this question.


Solution

  • Eric Niebler actually implemented the truncating version of this adaptor for his talk demonstrating range-v3 https://www.youtube.com/watch?v=mFUXNMfaciE.

    But it didn't make it into range-v3 https://github.com/ericniebler/range-v3/issues/776

    However, copy pasting from https://github.com/ericniebler/range-v3/blob/master/example/calendar.cpp we can just do

    std::vector<std::string> v1{ "aeiou", "bcdfghjk" };
    print2D(v1 | transpose());
    

    https://godbolt.org/z/vj6bbsWso

    To get the non-truncating version you could modify the copy pasted code.