I am learning Modern C++ and I am an adept of functional paradigm. Here is the situation: I want to calculate the rows in Pascal triangle using C++23 and its ranges
library.
Here is the implementation in Haskell (and I find the implementation extremely beautiful):
pascal 0 = [1]
pascal n = zipWith (+) (0:pascal (n-1)) (pascal (n-1) ++ [0])
The implementation comes from here.
The following is my attempt to implement it in C++23:
#include <iostream>
#include <vector>
#include <ranges>
namespace stdv = std::views;
std::vector<int> triangle_row(int n);
auto add = [](auto a, auto b) {return a + b; };
std::vector<int> triangle_row(int n) {
if (n == 0) {
return {1};
}
else {
auto left = (triangle_row(n-1)).insert(triangle_row(n-1).begin(), 0);
auto right = (triangle_row(n-1)).insert(triangle_row(n-1).end(),0);
auto tri_row = stdv::zip_transform(add, left, right);
return tri_row;
}
}
The else
part in the above implementation causes following error:
<source>: In function 'std::vector<int> triangle_row(int)':
<source>:17:41: error: no match for call to '(const std::ranges::views::_ZipTransform) (<lambda(auto:54, auto:55)>&, __gnu_cxx::__normal_iterator<int*, std::vector<int> >&, __gnu_cxx::__normal_iterator<int*, std::vector<int> >&)'
17 | auto tri_row = stdv::zip_transform(add, left, right);
| ~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~
...
How could I implement it (using functional paradigm; meaning no for-looping etc.) correctly and in a nicer way?
A fixed version might look like:
std::vector<int> triangle_row(int n) {
if (n == 0) {
return {1};
}
else {
auto left = triangle_row(n-1);
auto right = left; // Don't recalculate triangle_row(n-1)
left.insert(left.begin(), 0);
right.insert(right.end(), 0);
auto tri_row = stdv::zip_transform(add, left, right);
return {begin(tri_row), end(tri_row)};
}
}
Your insert
is wrong in several points:
std::vector
.C++26 would have std::views::concat
to allow lazy concatenation with auto left = std::views::concat(std::views::single(0), triangle_row(n-1));