Loops and iterations - algorithm

Is there a way that a function has 2 options of return and the option of return is chosen after a certain interval of iterations?
example:
a function that swaps from "a" to "b" and "b" to "a" after 4 iterations returns:
a
a
a
a
b
b
b
b
a
a
a
a
b
b
b
b
.
.
.
Edit I did something like this to solve the problem:
var counter = 0;
var state = true;
var changeState = function(){
var x = 0
while(x != 12){
if(counter == 4){
counter = 0;
state = !state;
}
if (state){
console.log("a");
} else {
console.log("b")
}
counter += 1;
x += 1
}
}
changeState();

You will need to have a stateful function, as it needs to somehow remember something from previous call(s) that were made to it. This is a so-called side effect, and means that the function is not pure.
For the example you have given, the function would need to know (i.e. have a state with) the number of previous calls (modulo 8), or the previous four returned values, or some mix of this information.
How such a function is implemented, depends on the programming language.
In object oriented programming languages you would create a class with the function as method, and a property reflecting that state. When the function is called, it also updates the state. For instance, in Java:
class MyClass {
int callCount = 0;
char myFunction() {
return "aaaabbbb".charAt(this.callCount++ % 8);
}
}
You would call the function repeatedly like so:
MyClass obj = new MyClass();
for (int i = 0; i < 10; i++) {
System.out.println(obj.myFunction());
}
In JavaScript you could do the same, or you could create a closure:
function createFunction() {
let callCount = 0;
return function myFunction() {
return "aaaabbbb"[callCount++ % 8];
}
}
let myFunction = createFunction();
for (let i = 0; i < 10; i++) {
console.log(myFunction());
}
In Python you can do the same, but it allows also to use default arguments (which are only initialised at function definition time), and so you could define an argument that is an object holding a counter:
def myFunction(counter=[0]):
counter[0] += 1
return "aaaabbbb"[counter[0] % 8]
for _ in range(10):
print(myFunction())
This is of course not an exhaustive list of possibilities, but the essence is that programming languages offer their own ways to construct such stateful functions.
Iterators
Some languages offer a different approach: iterators. This means the function produces a stream of return values. The function can run to produce the first value after which the running state is saved until a new value is requested from it. The function's execution context is then restored to run until it can produce the next value, ...etc.
Here is how that design would look in JavaScript:
function * myIterator() {
let callCount = 0;
while (true) {
yield "aaaabbbb"[callCount++ % 8];
// ^^^^^ a language construct for yielding back to the caller
}
}
let iter = myIterator();
for (let i = 0; i < 10; i++) {
console.log(iter.next().value);
}
When a programming language offers this possibility, it is often preferred over the other alternatives listed earlier.

Related

Paper cut algorithm

I want to create a function to determine the most number of pieces of paper on a parent paper size
The formula above is still not optimal. If using the above formula will only produce at most 32 cut/sheet.
I want it like below.
This seems to be a very difficult problem to solve optimally. See http://lagrange.ime.usp.br/~lobato/packing/ for a discussion of a 2008 paper claiming that the problem is believed (but not proven) to be NP-hard. The researchers found some approximation algorithms and implemented them on that website.
The following solution uses Top-Down Dynamic Programming to find optimal solutions to this problem. I am providing this solution in C#, which shouldn't be too hard to convert into the language of your choice (or whatever style of pseudocode you prefer). I have tested this solution on your specific example and it completes in less than a second (I'm not sure how much less than a second).
It should be noted that this solution assumes that only guillotine cuts are allowed. This is a common restriction for real-world 2D Stock-Cutting applications and it greatly simplifies the solution complexity. However, CS, Math and other programming problems often allow all types of cutting, so in that case this solution would not necessarily find the optimal solution (but it would still provide a better heuristic answer than your current formula).
First, we need a value-structure to represent the size of the starting stock, the desired rectangle(s) and of the pieces cut from the stock (this needs to be a value-type because it will be used as the key to our memoization cache and other collections, and we need to to compare the actual values rather than an object reference address):
public struct Vector2D
{
public int X;
public int Y;
public Vector2D(int x, int y)
{
X = x;
Y = y;
}
}
Here is the main method to be called. Note that all values need to be in integers, for the specific case above this just means multiplying everything by 100. These methods here require integers, but are otherwise are scale-invariant so multiplying by 100 or 1000 or whatever won't affect performance (just make sure that the values don't overflow an int).
public int SolveMaxCount1R(Vector2D Parent, Vector2D Item)
{
// make a list to hold both the item size and its rotation
List<Vector2D> itemSizes = new List<Vector2D>();
itemSizes.Add(Item);
if (Item.X != Item.Y)
{
itemSizes.Add(new Vector2D(Item.Y, Item.X));
}
int solution = SolveGeneralMaxCount(Parent, itemSizes.ToArray());
return solution;
}
Here is an example of how you would call this method with your parameter values. In this case I have assumed that all of the solution methods are part of a class called SolverClass:
SolverClass solver = new SolverClass();
int count = solver.SolveMaxCount1R(new Vector2D(2500, 3800), new Vector2D(425, 550));
//(all units are in tenths of a millimeter to make everything integers)
The main method calls a general solver method for this type of problem (that is not restricted to just one size rectangle and its rotation):
public int SolveGeneralMaxCount(Vector2D Parent, Vector2D[] ItemSizes)
{
// determine the maximum x and y scaling factors using GCDs (Greastest
// Common Divisor)
List<int> xValues = new List<int>();
List<int> yValues = new List<int>();
foreach (Vector2D size in ItemSizes)
{
xValues.Add(size.X);
yValues.Add(size.Y);
}
xValues.Add(Parent.X);
yValues.Add(Parent.Y);
int xScale = NaturalNumbers.GCD(xValues);
int yScale = NaturalNumbers.GCD(yValues);
// rescale our parameters
Vector2D parent = new Vector2D(Parent.X / xScale, Parent.Y / yScale);
var baseShapes = new Dictionary<Vector2D, Vector2D>();
foreach (var size in ItemSizes)
{
var reducedSize = new Vector2D(size.X / xScale, size.Y / yScale);
baseShapes.Add(reducedSize, reducedSize);
}
//determine the minimum values that an allowed item shape can fit into
_xMin = int.MaxValue;
_yMin = int.MaxValue;
foreach (var size in baseShapes.Keys)
{
if (size.X < _xMin) _xMin = size.X;
if (size.Y < _yMin) _yMin = size.Y;
}
// create the memoization cache for shapes
Dictionary<Vector2D, SizeCount> shapesCache = new Dictionary<Vector2D, SizeCount>();
// find the solution pattern with the most finished items
int best = solveGMC(shapesCache, baseShapes, parent);
return best;
}
private int _xMin;
private int _yMin;
The general solution method calls a recursive worker method that does most of the actual work.
private int solveGMC(
Dictionary<Vector2D, SizeCount> shapeCache,
Dictionary<Vector2D, Vector2D> baseShapes,
Vector2D sheet )
{
// have we already solved this size?
if (shapeCache.ContainsKey(sheet)) return shapeCache[sheet].ItemCount;
SizeCount item = new SizeCount(sheet, 0);
if ((sheet.X < _xMin) || (sheet.Y < _yMin))
{
// if it's too small in either dimension then this is a scrap piece
item.ItemCount = 0;
}
else // try every way of cutting this sheet (guillotine cuts only)
{
int child0;
int child1;
// try every size of horizontal guillotine cut
for (int c = sheet.X / 2; c > 0; c--)
{
child0 = solveGMC(shapeCache, baseShapes, new Vector2D(c, sheet.Y));
child1 = solveGMC(shapeCache, baseShapes, new Vector2D(sheet.X - c, sheet.Y));
if (child0 + child1 > item.ItemCount)
{
item.ItemCount = child0 + child1;
}
}
// try every size of vertical guillotine cut
for (int c = sheet.Y / 2; c > 0; c--)
{
child0 = solveGMC(shapeCache, baseShapes, new Vector2D(sheet.X, c));
child1 = solveGMC(shapeCache, baseShapes, new Vector2D(sheet.X, sheet.Y - c));
if (child0 + child1 > item.ItemCount)
{
item.ItemCount = child0 + child1;
}
}
// if no children returned finished items, then the sheet is
// either scrap or a finished item itself
if (item.ItemCount == 0)
{
if (baseShapes.ContainsKey(item.Size))
{
item.ItemCount = 1;
}
else
{
item.ItemCount = 0;
}
}
}
// add the item to the cache before we return it
shapeCache.Add(item.Size, item);
return item.ItemCount;
}
Finally, the general solution method uses a GCD function to rescale the dimensions to achieve scale-invariance. This is implemented in a static class called NaturalNumbers. I have included the rlevant parts of this class below:
static class NaturalNumbers
{
/// <summary>
/// Returns the Greatest Common Divisor of two natural numbers.
/// Returns Zero if either number is Zero,
/// Returns One if either number is One and both numbers are >Zero
/// </summary>
public static int GCD(int a, int b)
{
if ((a == 0) || (b == 0)) return 0;
if (a >= b)
return gcd_(a, b);
else
return gcd_(b, a);
}
/// <summary>
/// Returns the Greatest Common Divisor of a list of natural numbers.
/// (Note: will run fastest if the list is in ascending order)
/// </summary>
public static int GCD(IEnumerable<int> numbers)
{
// parameter checks
if (numbers == null || numbers.Count() == 0) return 0;
int first = numbers.First();
if (first <= 1) return 0;
int g = (int)first;
if (g <= 1) return g;
int i = 0;
foreach (int n in numbers)
{
if (i == 0)
g = n;
else
g = GCD(n, g);
if (g <= 1) return g;
i++;
}
return g;
}
// Euclidian method with Euclidian Division,
// From: https://en.wikipedia.org/wiki/Euclidean_algorithm
private static int gcd_(int a, int b)
{
while (b != 0)
{
int t = b;
b = (a % b);
a = t;
}
return a;
}
}
Please let me know of any problems or questions you might have with this solution.
Oops, forgot that I was also using this class:
public class SizeCount
{
public Vector2D Size;
public int ItemCount;
public SizeCount(Vector2D itemSize, int itemCount)
{
Size = itemSize;
ItemCount = itemCount;
}
}
As I mentioned in the comments, it would actually be pretty easy to factor this class out of the code, but it's still in there right now.

p5.js Recursive Bubble Sort

I'm trying to modify this solution to work for Bubble Sort, but I'm a bit out of my depth, especially with the whole async function business. The code works up to a point, but does not follow the exact pattern I would expect for Bubble Sort, and only partially sorts the array.
Can anyone help me out please?
let values = [];
let startSort = true;
function bubbleSort( a ) {
// create copy of the array
clone = a.slice();
// asynchronous sort the copy
recursiveBubbleSort( clone, clone.length );
return;
}
//Recursive Bubble Sort
async function recursiveBubbleSort( arr, n ) {
//If there is only single element
//the return the array
if ( n === 1 ) {
return arr;
}
await recursiveBubbleSort( arr, n - 1 );
//Swap the elements by comparing them
for ( let j = 0; j < n - 1; j++ ) {
if ( arr[j] > arr[j + 1] ) {
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
}
}
// copy back the current state of the sorting
values = arr.slice();
// slow down
await sleep( 500 );
}
async function sleep( ms ) {
return new Promise( resolve => setTimeout( resolve, ms ) );
}
function setup() {
createCanvas( 600, 190 );
frameRate( 60 );
}
let numOfRects = 15;
let rectWidth;
function draw() {
if ( startSort ) {
startSort = false;
rectWidth = floor( width / numOfRects );
values = new Array( floor( width / rectWidth ) );
for ( let i = 0; i < values.length; i++ ) {
values[i] = random( height );
}
bubbleSort( values );
}
background( 23 );
stroke( 0 );
fill( 255 );
for ( let i = 0; i < values.length; i++ ) {
rect( i * rectWidth, height - values[i], rectWidth, values[i] );
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>
In this answer I will not focus on how async and await work because that doesn't seem to be your goal. I'll show you how to make a working version of the bubble sort instead.
First let's get rid of this startSort variable you have: You use it to initialize your values and a better way to do it is to use the setup() function used by p5:
function setup() {
createCanvas(600, 190);
rectWidth = floor(width / numOfRects);
// Generate the values
values = new Array(floor(width / rectWidth));
for (let i = 0; i < values.length; i++) {
values[i] = random(height);
}
// The number of iterations is equal to the number of values
n = values.length;
}
We first we fill your array with random values and define a global variable n which will hold the remaining number of iterations do to (which is the same as the number of values).
Then let's modify your bubble sort function. Here we don't need it to be recursive because, as I'll show later on, we will simply call it several times until we have done all the iterations.
// Bubble Sort
function bubbleSort(arr, n) {
// If there is no remaining iterations do nothing
if (n <= 1) {
return 0;
}
// Swap the elements by comparing them
for (let j = 0; j < n - 1; j++) {
if (arr[j] > arr[j + 1]) {
[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
}
}
// We did one more iteration return the remaining number of iterations
return n - 1;
}
There are 3 important things here:
The function modifies the arr in place: so when you call the function with an array you will have the result in the same array.
The function returns the remaining number of iterations do it (each call to the function is one iteration)
If you call it with n the number of remaining of iterations at 1 or 0 it will simply do nothing. Otherwise it will make one pass on the array.
Now you can change your draw() function. This function is called automatically by p5 X times by seconds (by default 60). So each time it is ran it will call the bubble sort function, updating the array and the number of remaining iterations:
function draw() {
// Define the "speed" at which we do the iterations
frameRate(10);
background(23);
stroke(0);
fill(255);
// Show the values
for (let i = 0; i < values.length; i++) {
rect(i * rectWidth, height - values[i], rectWidth, values[i]);
}
// Make one new iteration
n = bubbleSort(values, n);
}
Note how we used frameRate() to call it less often to let the user see the different steps of the sort.
All you need is to declare your global variables at the beginning of your sketch, put everything together and you are good to go.
let values = [];
let numOfRects = 15;
let rectWidth;
let n;
You can see the complete code here you will notice that I did to more things in this implementation:
I extracted the code which puts new values in the array in it's own function resetArray()
I call this function in the setup() function to initialize the animation but also in draw() when n===0 so that when the array is sorted we generate new values to have an infinite animation.
A note about async and await. These functions are used to create asynchronous code in javascript. This is a whole topic which is not trivial for someone new to programming, you can read the doc here. Basically async is used to say that a function will take time to be executed and await is used to say to wait for the function to finish it's execution.
In the function you go inspiration from, this asynchronous code is used to put a delay between two calls to the bubbleSort function so that the user can see the different iterations. Here you don't really need it because p5 gives you the mechanism of draw() and frameRate() to handle that more easily.

CFSCRIPT - For loop increments the index wrong

I might have been found a bug in ColdFusion 2016.
I have two functions. The first one has a loop which iterates from 1 to n and pushes the return value of the second function which is also an array into an array. I noticed that;
index value is 1,
calling function and pushing the value into the array,
and index value is the end value of the loop.
Is this a bug?
<cfscript>
public array function fnc1(required array p1, required array p2, required numeric pSize, required numeric qSize, required numeric dSize){
iterationNum = pSize/2;
point = randRange(1, qSize-1);
for(i = 1; i <= iterationNum; i++){
writeOutput(i); // prints: 1
pop[i] = fnc2(p1[i], p2[i], point);
writeOutput(i); // prints: iterationNum value
writeDump(var = pop[i], label = "pop-"&i);
}
writeDump(var = pop, label="pop");
}
public array function fnc2(required array p1, required array p2, required numeric point){
n = arrayLen(p1);
concatArr = arrayNew(1);
for(i = 1; i <= point; i++){
concatArr[i] = p1[i];
}
for(i = point + 1; i <= n; i++){
concatArr[i] = p2[i];
}
writeDump(var=concatArr, label="Concated Array");
return concatArr;
}
</cfscript>
The default scope of a variable inside of a cfc is not function only. But rather it is cfc wide. This is often problematic.
Similarly, the default scope of a variable outside of a cfc is request wide. This is often useful.
Two approaches
There are two approaches to limit the scope of a variable inside of a cfc. One is to use the keyword var, the other is the use local.
It is a long story as to how they are different. The sample solution below uses var throughout. If you want to know more about var vs local., click here: Scoping: Local vs Var
<cfscript>
public array function fnc1(required array p1, required array p2, required numeric pSize, required numeric qSize, required numeric dSize){
var iterationNum = pSize/2;
var point = randRange(1, qSize-1);
for(var i = 1; i <= iterationNum; i++){
writeOutput(i); // prints: 1
pop[i] = fnc2(p1[i], p2[i], point);
writeOutput(i); // prints: iterationNum value
writeDump(var = pop[i], label = "pop-"&i);
}
writeDump(var = pop, label="pop");
}
public array function fnc2(required array p1, required array p2, required numeric point){
var n = arrayLen(p1);
var concatArr = arrayNew(1);
for(var i = 1; i <= point; i++){
concatArr[i] = p1[i];
}
for(var ii = point + 1; ii <= n; ii++){
concatArr[ii] = p2[ii];
}
writeDump(var=concatArr, label="Concated Array");
return concatArr;
}
</cfscript>

How to construct a 2-dimensional array in ATS?

For instance, I am looking for an example in ATS that does more or less what the following C code does:
int *theMultable[10][10];
void
theMultable_initialize()
{
int i, j;
for (i = 0; i < 10; i++)
{
for (j = 0; j < 10; j++) theMultable[i][j] := i * j;
}
return;
}
One possible approach is to attempt a direct translation to C. However, I now think that I should have used builtin matrix type instead. This code relies on quite a bit of advanced functionality (I even left one unproven lemma for exercise: it shows that N*sizeof(T) == sizeof(#[T][N]).
The loop to initialize a 2-dimensional array is implemented in the function:
extern
fun
multable_init (
mat: &(#[#[int][10]][10])? >> _
): void // end of [multable_init]
This function, in turn, uses two functions (both initialize an array of elements, basically). Also, the global variable multable is allocated, and is then initialized using multable_init (I thought it wouldn't work, but it did!).
Here's the code of initialization of the global variable:
var multable : #[int?][100]
val p_multable = addr#multable
prval pf_multable = array_v_group (view#multable)
val () = multable_init (!p_multable)
prval pf_multable = array_v_ungroup (pf_multable)
prval pf_multable = array2matrix_v (pf_multable)
val theMultable = ref_make_viewptr {matrix (int, 10, 10)} (pf_multable | p_multable)
A mutable array is allocated on stack, then we take its address (line 2), turns its corresponding at-proof from #[int?][100] to #[#[int?][10]][10] (via grouping on line 3), and initialize it. Then, we turn the grouped-array view into a matrix view, and finally, put it into a ref-cell.
The full code is at Glot.io

Return the cross product of a list of lists

Given a list of size n, Write a program that returns all possible combination of elements contained in each list.
Example:
List A = "x, z"
List B = "a, b, c"
List C = "o, p"
Output:
x a o
x a p
x b o
x b p
.....
z c p
Order doesn't matter, but the hard part is: You can't use recursion.
My solution:
void combos(const char *string)
{
int i, j, k;
int len = strlen(string);
for (i = 0; i < len - 2; i++)
{
for (j = i + 1; j < len - 1; j++)
{
for (k = j + 1; k < len; k++)
printf("%c%c%c\n", string[i], string[j], string[k]);
}
}
}
As you can see, it only works if I know the number of lists before hand. I am curious if recursion is the only way to solve it.
Think of it like how you increment a number, e.g. a base 3 number would go:
000
001
002
010
011
...
222
Now think of each digit being the index into each of the nested lists. You will have as many digits as you have nested lists, i.e. the size of the outer list.
The "base" of each digit may differ, and is the size of the corresponding nested list. A "digit" can be a very large number if a nested list is large.
So, you start by creating a list of "digit", or index values, initializing them to 0. You then print the values of the elements at those indices. You then increment the last index value, rolling over as needed, like you would a normal number, stopping when the first index value rolls over.
Here is a Java implementation using arrays of arrays, i.e. String[][]. You can easily change to List<List<String>> or List<String[]> if needed.
#SafeVarargs
public static void printCombos(String[] ... lists) {
if (lists.length == 0)
throw new IllegalArgumentException("No lists given");
for (String[] list : lists)
if (list.length == 0)
throw new IllegalArgumentException("List is empty");
int[] idx = new int[lists.length];
for (;;) {
// Print combo
for (int i = 0; i < lists.length; i++) {
if (i != 0)
System.out.print(' ');
System.out.print(lists[i][idx[i]]);
}
System.out.println();
// Advance to next combination
for (int i = lists.length - 1; ++idx[i] == lists[i].length; ) {
idx[i] = 0;
if (--i < 0)
return; // We're done
}
}
}
public static void main(String[] args) {
String[][] data = { { "x", "z" }, { "a", "b", "c" }, { "o", "p" } };
printCombos(data);
}
OUTPUT
x a o
x a p
x b o
x b p
x c o
x c p
z a o
z a p
z b o
z b p
z c o
z c p
If you use lists instead of arrays, then the code will use get(int), which may not always be good for performance, e.g. for LinkedList.
If that is the case, replace int[] idx with an Iterator[], initializing each array entry with an iterator for the corresponding list. Resetting a "digit" to 0 would then be done by retrieving a new Iterator from the list in question.
In this case, they don't even have to be lists, but can be any kind of collection, or more specifically Iterable objects.
You do not need recursion. All you need to do is build up a set of intermediate solutions. Here is a non-recursive solution in Python:
# This does NOT use recursion!
def all_comb(list_of_lists):
# We start with a list of just the empty set.
answer = [[]]
for list in list_of_lists:
# new_answer will be the list of combinations including this one.
new_answer = []
# Build up the new answer.
for thing in list:
for prev_list in answer:
new_answer.append(prev_list + [thing])
# Replace the old answer with the new one.
answer = new_answer
# We now have all combinations of all lists.
return answer
# Demonstration that it works.
for comb in all_comb([["x", "y"], ["a", "b", "c"], ["o", "p"]]):
print(" ".join(comb))
Edit: I'm answering this in python, because, although it's currently tagged language-agnostic, python is a good, executable pseudo-pseudocode.
If you can write the function in a form that is Tail-recursive, i.e. in a form that looks like def f(x): return f(g(x)), it's easy to turn it into an iterative form. Unfortunately, you usually won't end up with a tail-recursive call, so you need to know a couple of tricks.
First of all, let's say we have a function that looks like this:
def my_map(func, my_list):
if not my_list:
return []
return [func(my_list[0])] + change_my_list(my_list[1:])
Ok, so it's recursive, but not tail recursive: it's really
def my_map(func, my_list):
if not my_list:
return []
result = [func(my_list[0])] + change_my_list(my_list[1:])
return result
Instead, we need to adjust the function slightly, adding what is traditionally known as an accumulator:
def my_map(func, my_list, acc = [])
if not my_list: return acc
acc = acc + func(my_list[0])
return my_map(func, my_list[1:], acc + func(my_list[0]))
Now, we have a truly tail-recursive function: we've gone from def f(x): return g(f(x)) to def f(x): return f(g(x))
Now, it's quite simple to turn that function into a non-recursive form:
def my_map(func, my_list, acc=[]):
while True: #added
if not my_list: return acc
#return my_map(func, my_list[1:], acc + func(my_list[0])) #deleted
func, my_list, acc = func, my_list[1:], acc + func(my_list[0]) #added
Now, we just tidy up a little bit:
def my_map(func, my_list):
acc = []
while my_list:
acc.append(func(my_list[0])
my_list = my_list[1:]
return acc
Note you can clean it up even further using a for loop or a list comprehension, but that's left as an exercise for the reader.
Ok, so this was a pathological example, hopefully you'd know that python has a builtin map function, but the process is the same: transform into a tail recursive form, replace the recursive call with argument reassignment, and tidy up.
So, if you have:
def make_products(list_of_lists):
if not list_of_lists: return []
first_list = list_of_lists[0]
rest = list_of_lists[1:]
return product_of(first_list, make_products(rest))
You can convert it into a tail recursive form
def make_products(list_of_lists, acc=[]):
if not list_of_lists: return acc
first_list = list_of_lists[0]
rest = list_of_lists[1:]
acc = product_of(acc, first_list)
return make_products(rest, acc)
Then, that simplifies to:
def make_products(list_of_lists):
acc=[]
while list_of_lists:
first_list = list_of_lists[0]
rest = list_of_lists[1:]
acc = product_of(acc, first_list)
list_of_lists = rest
return acc
Again, this can be cleaned up further, into a for loop:
def make_products(list_of_lists):
acc=[]
for lst in list_of_lists:
acc = product_of(acc, lst)
return acc
If you've looked at the builtin functions, you might notice this is somewhat familiar: it's essentially the reduce function:
def reduce(function, iterable, initializer):
acc = initializer
for x in iterable:
acc = function(acc, x)
return acc
So, the final form is something like
def make_products(list_of_lists):
return reduce(product_of, list_of_lists, []) # the last argument is actually optional here
You then just have to worry about writing the product_of function.
As you know, the usual solution is recursion. However, out of boredom I once wrote a java method multiNext to do this without recursion. multiNext uses an array to keep track of a load of indices in an equivalent system of nested loops.
public static boolean multiNext(int[] current, int[] slotLengths) {
for (int r = current.length - 1; r >= 0; r--) {
if (current[r] < slotLengths[r] - 1) {
current[r]++;
return true;
} else {
current[r] = 0;
}
}
return false;
}
public static void cross(List<List<String>> lists) {
int size = lists.size();
int[] current = new int[size];
int[] slotLengths = new int[size];
for (int i = 0; i < size; i++)
slotLengths[i] = lists.get(i).size();
do {
List<String> temp = new ArrayList<>();
for (int i = 0; i < size; i++)
temp.add(lists.get(i).get(current[i]));
System.out.println(temp);
} while (multiNext(current, slotLengths));
}
public static void main(String[] args) {
cross(Arrays.asList(Arrays.asList("x", "z"), Arrays.asList("a", "b", "c"), Arrays.asList("o", "p")));
}

Resources