Search code examples
c++functional-programmingc++23

Functional C++23 when computing the rows in Pascal triangle


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?


Solution

  • 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)};
       }
    }
    

    Demo

    Your insert is wrong in several points:

    • return type is iterator and not std::vector.
    • iterator to provide as first argument should belong to that container instance.

    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));