Search code examples
c++functional-programmingc++17cartesian-product

Doing cartesian product the functional way in C++17


I'm trying to implement cartesian product using a closure / custom function, the closure is function(x,y) = pow(x,2) + pow(y,2) and implement it the functional way i.e. without using the C-style loops.

Here is my take.

#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
void print (vector<int> aux) {
  vector<int>::iterator it = aux.begin();
  while(it != aux.end()){
    cout << *it << " ";
    it++; }}

int main() {
  vector<int> a{1,2};
  vector<int> b{3,4};
  vector<int> cartesian(4);
  transform (a.begin(), a.end(), b.begin(), cartesian.begin(), [](int &i)
         {vector<int>::iterator p = b.begin();
           for_each(begin(b), end(b), [](int &n) { return pow(i,2) + pow(n,2) })});
  print(cartesian);
  // desired output is: 10,17,13,20 (cartesian operation)
}

First, using each element of the first vector, iterate over the second vector , then store the result of applying the function in the result vector.

The code is for representation purposes. It doesn't compile. It gives 'b' is not captured in {vector<int>::iterator p = b.begin(); error, among others.

How to rectify this code and/or how to correctly implement cartesian operation with a closure?


Solution

  • Your compilation issues are because you are not capturing the needed variables in your lambdas, and you're missing some ;.

    However, a simpler way to do a cartesian product is with 2 nested loops, like this:

    for (int i : a)
      for (int j : b)
        cartesian.push_back(i * i + j * j);
    

    If you want to use algorithms, then you can write:

    for_each(begin(a), end(a), [&](int i) { 
      for_each(begin(b), end(b), [&](int j) { 
        cartesian.push_back(i * i + j * j); 
      });
    });
    

    although I find this harder to read, and doesn't really help much since for_each is an algorithm that happens to have an in-built language construct, similar to transform.


    From c++20, ranges and views have been added, and so you can get considerably more creative:

    namespace rs = std::ranges;
    namespace rv = std::views;
    
    auto product = [=](int i) 
    {
       auto  op = [=](int j) { return i * i + j * j;};
    
       return b | rv::transform(op);
    };
      
    rs::copy(a | rv::transform(product) 
               | rv::join,
             std::back_inserter(cartesian));
    

    Again, the original nested for loops are probably the best approach for a cartesian product, but this should give you a taste for the exciting possibilities.

    Here's a demo.