-
Notifications
You must be signed in to change notification settings - Fork 5.2k
/
try_all_permutations.js
71 lines (65 loc) · 2.12 KB
/
try_all_permutations.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// Given some functions, run them in every possible order.
//
// In simplest usage, takes one argument, an array of functions. Run
// those functions in every possible order. Or, if the first element
// of the array is an integer N, with the remaining elements being
// functions (N <= the number of functions), run every permutation of
// N functions from the array.
//
// Eg:
// try_all_permutations([A, B, C])
// => runs A, B, C; A, C, B; B, A, C; B, C, A; C, A, B; C, B, A
// (semicolons for clarity only)
//
// try_all_permutations([2, A, B, C])
// => runs A, B; A, C; B, A; B, C; C, A; C, B
//
// If more than one argument A_1, A_2 ... A_n is passed, each should
// be an array as described above. Compute the possible orderings O_1,
// O_2 ... O_n per above, and run the Cartesian product of the
// sets. (Except that unlike a proper Cartesian product, a set with
// zero elements will simply be ignored.)
//
// Eg:
// try_all_permutations([X], [A, B], [Y])
// => runs X, A, B, Y; X, B, A, Y
// try_all_permutations([X], [A, B], [], [Y])
// => same
//
// If a function is passed instead of an array, it will be treated as
// an array with one argument. In other words, these are the same:
// try_all_permutations([X], [A, B], [Y])
// try_all_permutations(X, [A, B], Y)
var try_all_permutations = function () {
var args = Array.prototype.slice.call(arguments);
var current_set = 0;
var chosen = [];
var expand_next_set = function () {
if (current_set === args.length) {
_.each(chosen, function (f) { f(); });
} else {
var set = args[current_set];
if (typeof set === "function")
set = [set];
current_set++;
if (typeof set[0] === "number")
pick(set[0], set.slice(1));
else
pick(set.length, set);
current_set--;
}
};
var pick = function (how_many, remaining) {
if (how_many === 0)
expand_next_set();
else {
for (var i = 0; i < remaining.length; i++) {
chosen.push(remaining[i]);
pick(how_many - 1,
remaining.slice(0, i).concat(remaining.slice(i + 1)));
chosen.pop();
}
}
};
expand_next_set();
};