3

I have a bunch of functions, a.process, b.process, c.process..., let's call them a, b, c... that all take a std::string as parameter and return a std::string.

Given an initial std::string s, I want to generate every possible output of combinations of a, b, c... called in any order and given s as initial input.

So for instance, I want to calculate a(s), b(s), c(s), a(b(s)), a(c(s)), b(a(s)), b(c(s)), c(a(s)), c(b(s)), a(b(c(s))), etc.

I think I could do a function to generate every permutation of a list, something like Python's itertools.permutations but I have two main issues here:

  • I don't just want every permutation, I want every permutation in every order.

  • And more important, I have no idea about how to store functions in arrays like one would easily do in Python.

Also I need that each possible output comes with the combinations of functions that generated it so for the example I gave above, I would know that outputs are in the following order: "a", "b", "c", "ab", "ac", "ba", "bc", "ca", "cb", "abc", etc.

How could I implement that in C++?

Yksuh
  • 47
  • 4

4 Answers4

1

For storing functions in an array, you need to use function pointers. Something like this would do:

typedef string (*t_fnPtr)(string);
t_fnPtr fnPtrArray[10]; //Initialize this with your functions

Then it's just a matter of generating all combinations of your array and applying it to the string. Look at this question.

Sid
  • 1,239
  • 2
  • 13
  • 36
1

I'm just fleshing out Sid's answer into actual code. Differing from Galik's answer, this doesn't produce duplicates

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>

using Fn = std::string (*)(std::string);

std::string apply_all(const std::vector<Fn>& fns, std::string s)
{
    for(auto fn : fns)
        s = fn(s);
    return s;
}

int main()
{
    std::string s = /* some string */;
    std::vector<Fn> functions{/* initialize with functions */};
    int n = functions.size();

    for(int r = 1; r <= n; r++)
    {
        std::vector<bool> v(n);
        std::fill(v.begin(), v.begin() + r, true);  // select r functions

        // permute through all possible selections of r functions
        do {
            std::vector<Fn> selected;
            for(int i = 0; i < n; ++i)
                if(v[i])
                    selected.push_back(functions[i]);

            std::sort(selected.begin(), selected.end());

            // permute through the r functions
            do {
                std::cout << apply_all(selected, s) << std::endl;
            } while(std::next_permutation(selected.begin(), selected.end()));
        } while(std::prev_permutation(v.begin(), v.end()));
    }
}

Live example

Passer By
  • 19,325
  • 6
  • 49
  • 96
  • How should I do it if my functions are extracted from instances? I cannot initialize the vector of functions with `{class1.a, class2.a, class3.a}` for example, but I cannot use `CLASS1::a` because my functions vary depending on instance initialization. – Yksuh Jun 11 '17 at 09:41
  • Change `Fn` to `std::function`, and initialize with `[&](std::string s){return class1.a(s);}`. [More on lambdas](http://en.cppreference.com/w/cpp/language/lambda) – Passer By Jun 11 '17 at 14:31
0

Some ideas and pseudocode

Array of function pointers, one for each function.

typedef std::string (*Func)(std::string const&);
Func const func_array[] = {&a, &b, &c};
int const n = sizeof array / sizeof array[0];  // number of functions

We need

 n choose 1 to get 1-element combinations a, b, c
 n choose 2 to get 2-element combinations ab, ac, bc
 n choose 3 to get 3-element combinations abc

Further, for each combination, we need all possible permutations. I.e.

 For all k = 1 to n, 
   All permutations of (All combinations of n-choose-k) 

Sketch

for i = 0 to 2^(n-1):
  xi = elements of func_array corresponding to the '1' bits in i
    used std::next_permutation() to generate all permutations of xi

See https://en.wikipedia.org/wiki/Power_set and http://en.cppreference.com/w/cpp/algorithm/next_permutation

Arun
  • 19,750
  • 10
  • 51
  • 60
0

If I have understood the problem right maybe something like this. It uses std::next_permutation to iterate through every ordering of processes and for every ordering it selects each sub-list or processes to operate (successively) on the input:

std::string process_1(std::string const& s)
    { return s + 'a'; }

std::string process_2(std::string const& s)
    { return s + 'b'; }

std::string process_3(std::string const& s)
    { return s + 'c'; }

int main(int, char** argv)
{
    using process = std::string(*)(std::string const&);


    std::vector<process> processes;

    processes.push_back(process_1);
    processes.push_back(process_2);
    processes.push_back(process_3);

    std::vector<std::string> outputs;

    std::sort(std::begin(processes), std::end(processes));

    do
    {
        for(auto p = std::begin(processes); p != std::end(processes); ++p)
        {
            std::string s = "";
            for(auto p1 = p; p1 != std::end(processes); ++p1)
                s = (*p1)(s);

            outputs.push_back(s);
        }
    }
    while(std::next_permutation(std::begin(processes), std::end(processes)));

    for(auto const& o: outputs)
        std::cout << o << '\n';
}

Output:

abc
bc
c
acb
cb
b
bac
ac
c
bca
ca
a
cab
ab
b
cba
ba
a

NOTE: It seems this method produces some duplicates. There should be a clever mathematical way to eliminate them that eludes me right now but you can always maintain a std::set<std::vector<process>> to record every combination that has already been tried to make sure there are no repeats. Or simply remove duplicates from the output.

Galik
  • 47,303
  • 4
  • 80
  • 117