////////////////////////////////////////////////////////////////////// // LibFile: lists.scad // Functions for constructing and manipulating generic lists. // Includes: // include ////////////////////////////////////////////////////////////////////// // Terminology: // **List** = An ordered collection of zero or more items. ie: `["a", "b", "c"]` // **Vector** = A list of numbers. ie: `[4, 5, 6]` // **Array** = A nested list of lists, or list of lists of lists, or deeper. ie: `[[2,3], [4,5], [6,7]]` // **Set** = A list of unique items. // Section: List Query Operations // Function: is_homogeneous() // Alias: is_homogenous() // Usage: // bool = is_homogeneous(list, depth); // Topics: List Handling, Type Checking // See Also: is_vector(), is_matrix() // Description: // Returns true when the list has elements of same type up to the depth `depth`. // Booleans and numbers are not distinguinshed as of distinct types. // Arguments: // l = the list to check // depth = the lowest level the check is done // Example: // a = is_homogeneous([[1,["a"]], [2,["b"]]]); // Returns true // b = is_homogeneous([[1,["a"]], [2,[true]]]); // Returns false // c = is_homogeneous([[1,["a"]], [2,[true]]], 1); // Returns true // d = is_homogeneous([[1,["a"]], [2,[true]]], 2); // Returns false // e = is_homogeneous([[1,["a"]], [true,["b"]]]); // Returns true function is_homogeneous(l, depth=10) = !is_list(l) || l==[] ? false : let( l0=l[0] ) [] == [for(i=[1:1:len(l)-1]) if( ! _same_type(l[i],l0, depth+1) ) 0 ]; function is_homogenous(l, depth=10) = is_homogeneous(l, depth); function _same_type(a,b, depth) = (depth==0) || (is_undef(a) && is_undef(b)) || (is_bool(a) && is_bool(b)) || (is_num(a) && is_num(b)) || (is_string(a) && is_string(b)) || (is_list(a) && is_list(b) && len(a)==len(b) && []==[for(i=idx(a)) if( ! _same_type(a[i],b[i],depth-1) ) 0] ); // Function: min_length() // Usage: // llen = min_length(array); // Topics: List Handling // See Also: max_length() // Description: // Returns the length of the shortest sublist in a list of lists. // Arguments: // array = A list of lists. // Example: // slen = min_length([[3,4,5],[6,7,8,9]]); // Returns: 3 function min_length(array) = assert(is_list(array), "Invalid input." ) min([for (v = array) len(v)]); // Function: max_length() // Usage: // llen = max_length(array); // Topics: List Handling // See Also: min_length() // Description: // Returns the length of the longest sublist in a list of lists. // Arguments: // array = A list of lists. // Example: // llen = max_length([[3,4,5],[6,7,8,9]]); // Returns: 4 function max_length(array) = assert(is_list(array), "Invalid input." ) max([for (v = array) len(v)]); // Function: in_list() // Usage: // bool = in_list(val, list, [idx]); // Topics: List Handling // Description: // Returns true if value `val` is in list `list`. When `val==NAN` the answer will be false for any list. // Arguments: // val = The simple value to search for. // list = The list to search. // idx = If given, searches the given columns for matches for `val`. // Example: // a = in_list("bar", ["foo", "bar", "baz"]); // Returns true. // b = in_list("bee", ["foo", "bar", "baz"]); // Returns false. // c = in_list("bar", [[2,"foo"], [4,"bar"], [3,"baz"]], idx=1); // Returns true. function in_list(val,list,idx) = assert( is_list(list) && (is_undef(idx) || is_finite(idx)), "Invalid input." ) let( s = search([val], list, num_returns_per_match=1, index_col_num=idx)[0] ) s==[] || s==[[]] ? false : is_undef(idx) ? val==list[s] : val==list[s][idx]; // Function: add_scalar() // Usage: // v = add_scalar(v, s); // Topics: List Handling // Description: // Given a list and a scalar, returns the list with the scalar added to each item in it. // If given a list of arrays, recursively adds the scalar to the each array. // Arguments: // v = The initial array. // s = A scalar value to add to every item in the array. // Example: // a = add_scalar([1,2,3],3); // Returns: [4,5,6] // b = add_scalar([[1,2,3],[3,4,5]],3); // Returns: [[4,5,6],[6,7,8]] function add_scalar(v,s) = is_finite(s) ? [for (x=v) is_list(x)? add_scalar(x,s) : is_finite(x) ? x+s: x] : v; // Section: List Indexing // Function: select() // Topics: List Handling // Description: // Returns a portion of a list, wrapping around past the beginning, if end=s) for (i=[s:1:e]) list[i]]; // Function: last() // Usage: // item = last(list); // Topics: List Handling // See Also: select(), slice(), column() // Description: // Returns the last element of a list, or undef if empty. // Arguments: // list = The list to get the last element of. // Example: // l = [3,4,5,6,7,8,9]; // x = last(l); // Returns 9. function last(list) = list[len(list)-1]; // Function: list_head() // Usage: // list = list_head(list, [to]); // Topics: List Handling // See Also: select(), slice(), list_tail(), last() // Description: // Returns the head of the given list, from the first item up until the `to` index, inclusive. // If the `to` index is negative, then the length of the list is added to it, such that // `-1` is the last list item. `-2` is the second from last. `-3` is third from last, etc. // If the list is shorter than the given index, then the full list is returned. // Arguments: // list = The list to get the head of. // to = The last index to include. If negative, adds the list length to it. ie: -1 is the last list item. // Example: // hlist1 = list_head(["foo", "bar", "baz"]); // Returns: ["foo", "bar"] // hlist2 = list_head(["foo", "bar", "baz"], -3); // Returns: ["foo"] // hlist3 = list_head(["foo", "bar", "baz"], 2); // Returns: ["foo","bar"] // hlist4 = list_head(["foo", "bar", "baz"], -5); // Returns: [] // hlist5 = list_head(["foo", "bar", "baz"], 5); // Returns: ["foo","bar","baz"] function list_head(list, to=-2) = assert(is_list(list)) assert(is_finite(to)) to<0? [for (i=[0:1:len(list)+to]) list[i]] : to=0? [for (i=[from:1:len(list)-1]) list[i]] : let(from = from + len(list)) from>=0? [for (i=[from:1:len(list)-1]) list[i]] : list; // Function: bselect() // Usage: // array = bselect(array, index); // Topics: List Handling // See Also: list_bset() // Description: // Returns the items in `array` whose matching element in `index` is true. // Arguments: // array = Initial list to extract items from. // index = List of booleans. // Example: // a = bselect([3,4,5,6,7], [false,true,true,false,true]); // Returns: [4,5,7] function bselect(array,index) = assert(is_list(array)||is_string(array), "Improper array." ) assert(is_list(index) && len(index)>=len(array) , "Improper index list." ) is_string(array)? str_join(bselect( [for (x=array) x], index)) : [for(i=[0:len(array)-1]) if (index[i]) array[i]]; // Section: List Construction // Function: repeat() // Usage: // list = repeat(val, n); // Topics: List Handling // See Also: count(), lerpn() // Description: // Generates a list or array of `n` copies of the given value `val`. // If the count `n` is given as a list of counts, then this creates a // multi-dimensional array, filled with `val`. // Arguments: // val = The value to repeat to make the list or array. // n = The number of copies to make of `val`. // Example: // a = repeat(1, 4); // Returns [1,1,1,1] // b = repeat(8, [2,3]); // Returns [[8,8,8], [8,8,8]] // c = repeat(0, [2,2,3]); // Returns [[[0,0,0],[0,0,0]], [[0,0,0],[0,0,0]]] // d = repeat([1,2,3],3); // Returns [[1,2,3], [1,2,3], [1,2,3]] function repeat(val, n, i=0) = is_num(n)? [for(j=[1:1:n]) val] : assert( is_list(n), "Invalid count number.") (i>=len(n))? val : [for (j=[1:1:n[i]]) repeat(val, n, i+1)]; // Function: count() // Usage: // list = count(n, [s], [step], [reverse]); // Description: // Creates a list of `n` numbers, starting at `s`, incrementing by `step` each time. // You can also pass a list for n and then the length of the input list is used. // Arguments: // n = The length of the list of numbers to create, or a list to match the length of // s = The starting value of the list of numbers. // step = The amount to increment successive numbers in the list. // reverse = Reverse the list. Default: false. // Example: // nl1 = count(5); // Returns: [0,1,2,3,4] // nl2 = count(5,3); // Returns: [3,4,5,6,7] // nl3 = count(4,3,2); // Returns: [3,5,7,9] // nl4 = count(5,reverse=true); // Returns: [4,3,2,1,0] // nl5 = count(5,3,reverse=true); // Returns: [7,6,5,4,3] function count(n,s=0,step=1,reverse=false) = let(n=is_list(n) ? len(n) : n) reverse? [for (i=[n-1:-1:0]) s+i*step] : [for (i=[0:1:n-1]) s+i*step]; // Function: list_bset() // Usage: // arr = list_bset(indexset, valuelist, [dflt]); // Topics: List Handling // See Also: bselect() // Description: // Opposite of `bselect()`. Returns a list the same length as `indexlist`, where each item will // either be 0 if the corresponding item in `indexset` is false, or the next sequential value // from `valuelist` if the item is true. The number of `true` values in `indexset` must be equal // to the length of `valuelist`. // Arguments: // indexset = A list of boolean values. // valuelist = The list of values to set into the returned list. // dflt = Default value to store when the indexset item is false. // Example: // a = list_bset([false,true,false,true,false], [3,4]); // Returns: [0,3,0,4,0] // b = list_bset([false,true,false,true,false], [3,4], dflt=1); // Returns: [1,3,1,4,1] function list_bset(indexset, valuelist, dflt=0) = assert(is_list(indexset), "The index set is not a list." ) assert(is_list(valuelist), "The `valuelist` is not a list." ) let( trueind = search([true], indexset,0)[0] ) assert( !(len(trueind)>len(valuelist)), str("List `valuelist` too short; its length should be ",len(trueind)) ) assert( !(len(trueind)=0.5) list[i]] ) concat(shuffle(left), shuffle(right)); // Function: repeat_entries() // Usage: // newlist = repeat_entries(list, N, [exact]); // Topics: List Handling // See Also: repeat() // Description: // Takes a list as input and duplicates some of its entries to produce a list // with length `N`. If the requested `N` is not a multiple of the list length then // the entries will be duplicated as uniformly as possible. You can also set `N` to a vector, // in which case len(N) must equal len(list) and the output repeats the ith entry N[i] times. // In either case, the result will be a list of length `N`. The `exact` option requires // that the final length is exactly as requested. If you set it to `false` then the // algorithm will favor uniformity and the output list may have a different number of // entries due to rounding. // . // When applied to a path the output path is the same geometrical shape but has some vertices // repeated. This can be useful when you need to align paths with a different number of points. // (See also subdivide_path for a different way to do that.) // Arguments: // list = list whose entries will be repeated // N = scalar total number of points desired or vector requesting N[i] copies of vertex i. // exact = if true return exactly the requested number of points, possibly sacrificing uniformity. If false, return uniform points that may not match the number of points requested. Default: True // Example: // list = [0,1,2,3]; // a = repeat_entries(list, 6); // Returns: [0,0,1,2,2,3] // b = repeat_entries(list, 6, exact=false); // Returns: [0,0,1,1,2,2,3,3] // c = repeat_entries(list, [1,1,2,1], exact=false); // Returns: [0,1,2,2,3] function repeat_entries(list, N, exact=true) = assert(is_list(list) && len(list)>0, "The list cannot be void.") assert((is_finite(N) && N>0) || is_vector(N,len(list)), "Parameter N must be a number greater than zero or vector with the same length of `list`") let( length = len(list), reps_guess = is_list(N)? N : repeat(N/length,length), reps = exact ? _sum_preserving_round(reps_guess) : [for (val=reps_guess) round(val)] ) [for(i=[0:length-1]) each repeat(list[i],reps[i])]; // Function: list_set() // Usage: // list = list_set(list, indices, values, [dflt], [minlen]); // Topics: List Handling // See Also: list_insert(), list_remove(), list_remove_values() // Description: // Takes the input list and returns a new list such that `list[indices[i]] = values[i]` for all of // the (index,value) pairs supplied and unchanged for other indices. If you supply `indices` that are // beyond the length of the list then the list is extended and filled in with the `dflt` value. // If you set `minlen` then the list is lengthed, if necessary, by padding with `dflt` to that length. // Repetitions in `indices` are not allowed. The lists `indices` and `values` must have the same length. // If `indices` is given as a scalar, then that index of the given `list` will be set to the scalar value of `values`. // Arguments: // list = List to set items in. Default: [] // indices = List of indices into `list` to set. // values = List of values to set. // dflt = Default value to store in sparse skipped indices. // minlen = Minimum length to expand list to. // Example: // a = list_set([2,3,4,5], 2, 21); // Returns: [2,3,21,5] // b = list_set([2,3,4,5], [1,3], [81,47]); // Returns: [2,81,4,47] function list_set(list=[],indices,values,dflt=0,minlen=0) = assert(is_list(list)) !is_list(indices)? ( (is_finite(indices) && indices length ? list_trim(array,length) : list_pad(array,length,fill); // Section: Iteration Helpers // Function: idx() // Usage: // rng = idx(list, [s=], [e=], [step=]); // for(i=idx(list, [s=], [e=], [step=])) ... // Topics: List Handling, Iteration // See Also: enumerate(), pair(), triplet(), combinations(), permutations() // Description: // Returns the range of indexes for the given list. // Arguments: // list = The list to returns the index range of. // s = The starting index. Default: 0 // e = The delta from the end of the list. Default: -1 (end of list) // step = The step size to stride through the list. Default: 1 // Example(2D): // colors = ["red", "green", "blue"]; // for (i=idx(colors)) right(20*i) color(colors[i]) circle(d=10); function idx(list, s=0, e=-1, step=1) = assert(is_list(list)||is_string(list), "Invalid input." ) let( ll = len(list) ) ll == 0 ? [0:1:ll-1] : let( _s = posmod(s,ll), _e = posmod(e,ll) ) [_s : step : _e]; // Function: enumerate() // Usage: // arr = enumerate(l, [idx]); // for (x = enumerate(l, [idx])) ... // x[0] is the index number, x[1] is the item. // Topics: List Handling, Iteration // See Also: idx(), pair(), triplet(), combinations(), permutations() // Description: // Returns a list, with each item of the given list `l` numbered in a sublist. // Something like: `[[0,l[0]], [1,l[1]], [2,l[2]], ...]` // Arguments: // l = List to enumerate. // idx = If given, enumerates just the given columns items of `l`. // Example: // enumerate(["a","b","c"]); // Returns: [[0,"a"], [1,"b"], [2,"c"]] // enumerate([[88,"a"],[76,"b"],[21,"c"]], idx=1); // Returns: [[0,"a"], [1,"b"], [2,"c"]] // enumerate([["cat","a",12],["dog","b",10],["log","c",14]], idx=[1:2]); // Returns: [[0,"a",12], [1,"b",10], [2,"c",14]] // Example(2D): // colors = ["red", "green", "blue"]; // for (p=enumerate(colors)) right(20*p[0]) color(p[1]) circle(d=10); function enumerate(l,idx=undef) = assert(is_list(l)||is_string(list), "Invalid input." ) assert( _valid_idx(idx,0,len(l)), "Invalid index/indices." ) (idx==undef) ? [for (i=[0:1:len(l)-1]) [i,l[i]]] : [for (i=[0:1:len(l)-1]) [ i, for (j=idx) l[i][j]] ]; // Function: pair() // Usage: // p = pair(list, [wrap]); // for (p = pair(list, [wrap])) ... // On each iteration, p contains a list of two adjacent items. // Topics: List Handling, Iteration // See Also: idx(), enumerate(), triplet(), combinations(), permutations() // Description: // Takes a list, and returns a list of adjacent pairs from it, optionally wrapping back to the front. // Arguments: // list = The list to iterate. // wrap = If true, wrap back to the start from the end. ie: return the last and first items as the last pair. Default: false // Example(2D): Does NOT wrap from end to start, // for (p = pair(circle(d=40, $fn=12))) // stroke(p, endcap2="arrow2"); // Example(2D): Wraps around from end to start. // for (p = pair(circle(d=40, $fn=12), wrap=true)) // stroke(p, endcap2="arrow2"); // Example: // l = ["A","B","C","D"]; // echo([for (p=pair(l)) str(p.y,p.x)]); // Outputs: ["BA", "CB", "DC"] function pair(list, wrap=false) = assert(is_list(list)||is_string(list), "Invalid input." ) assert(is_bool(wrap)) let( ll = len(list) ) wrap ? [for (i=[0:1:ll-1]) [list[i], list[(i+1) % ll]]] : [for (i=[0:1:ll-2]) [list[i], list[i+1]]]; // Function: triplet() // Usage: // list = triplet(list, [wrap]); // for (t = triplet(list, [wrap])) ... // Topics: List Handling, Iteration // See Also: idx(), enumerate(), pair(), combinations(), permutations() // Description: // Takes a list, and returns a list of adjacent triplets from it, optionally wrapping back to the front. // Example: // l = ["A","B","C","D","E"]; // echo([for (p=triplet(l)) str(p.z,p.y,p.x)]); // Outputs: ["CBA", "DCB", "EDC"] // Example(2D): // path = [for (i=[0:24]) polar_to_xy(i*2, i*360/12)]; // for (t = triplet(path)) { // a = t[0]; b = t[1]; c = t[2]; // v = unit(unit(a-b) + unit(c-b)); // translate(b) rot(from=FWD,to=v) anchor_arrow2d(); // } // stroke(path); function triplet(list, wrap=false) = assert(is_list(list)||is_string(list), "Invalid input." ) assert(is_bool(wrap)) let( ll = len(list) ) wrap ? [for (i=[0:1:ll-1]) [ list[i], list[(i+1)%ll], list[(i+2)%ll] ]] : [for (i=[0:1:ll-3]) [ list[i], list[i+1], list[i+2] ]]; // Function: combinations() // Usage: // list = combinations(l, [n]); // Topics: List Handling, Iteration // See Also: idx(), enumerate(), pair(), triplet(), permutations() // Description: // Returns a list of all of the (unordered) combinations of `n` items out of the given list `l`. // For the list `[1,2,3,4]`, with `n=2`, this will return `[[1,2], [1,3], [1,4], [2,3], [2,4], [3,4]]`. // For the list `[1,2,3,4]`, with `n=3`, this will return `[[1,2,3], [1,2,4], [1,3,4], [2,3,4]]`. // Arguments: // l = The list to provide permutations for. // n = The number of items in each permutation. Default: 2 // Example: // pairs = combinations([3,4,5,6]); // Returns: [[3,4],[3,5],[3,6],[4,5],[4,6],[5,6]] // triplets = combinations([3,4,5,6],n=3); // Returns: [[3,4,5],[3,4,6],[3,5,6],[4,5,6]] // Example(2D): // for (p=combinations(regular_ngon(n=7,d=100))) stroke(p); function combinations(l,n=2,_s=0) = assert(is_list(l), "Invalid list." ) assert( is_finite(n) && n>=1 && n<=len(l), "Invalid number `n`." ) n==1 ? [for (i=[_s:1:len(l)-1]) [l[i]]] : [for (i=[_s:1:len(l)-n], p=combinations(l,n=n-1,_s=i+1)) concat([l[i]], p)]; // Function: permutations() // Usage: // list = permutations(l, [n]); // Topics: List Handling, Iteration // See Also: idx(), enumerate(), pair(), triplet(), combinations() // Description: // Returns a list of all of the (ordered) permutation `n` items out of the given list `l`. // For the list `[1,2,3]`, with `n=2`, this will return `[[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]]` // For the list `[1,2,3]`, with `n=3`, this will return `[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]` // Arguments: // l = The list to provide permutations for. // n = The number of items in each permutation. Default: 2 // Example: // pairs = permutations([3,4,5,6]); // // Returns: [[3,4],[3,5],[3,6],[4,3],[4,5],[4,6],[5,3],[5,4],[5,6],[6,3],[6,4],[6,5]] function permutations(l,n=2) = assert(is_list(l), "Invalid list." ) assert( is_finite(n) && n>=1 && n<=len(l), "Invalid number `n`." ) n==1 ? [for (i=[0:1:len(l)-1]) [l[i]]] : [for (i=idx(l), p=permutations([for (j=idx(l)) if (i!=j) l[j]], n=n-1)) concat([l[i]], p)]; // Section: Changing list structure // Internal. Not exposed. function _array_dim_recurse(v) = !is_list(v[0]) ? len( [for(entry=v) if(!is_list(entry)) 0] ) == 0 ? [] : [undef] : let( firstlen = is_list(v[0]) ? len(v[0]): undef, first = len( [for(entry = v) if(! is_list(entry) || (len(entry) != firstlen)) 0 ] ) == 0 ? firstlen : undef, leveldown = flatten(v) ) is_list(leveldown[0]) ? concat([first],_array_dim_recurse(leveldown)) : [first]; function _array_dim_recurse(v) = let( alen = [for(vi=v) is_list(vi) ? len(vi): -1] ) v==[] || max(alen)==-1 ? [] : let( add = max(alen)!=min(alen) ? undef : alen[0] ) concat( add, _array_dim_recurse(flatten(v))); // Function: array_dim() // Usage: // dims = array_dim(v, [depth]); // Topics: Matrices, Array Handling // Description: // Returns the size of a multi-dimensional array. Returns a list of dimension lengths. The length // of `v` is the dimension `0`. The length of the items in `v` is dimension `1`. The length of the // items in the items in `v` is dimension `2`, etc. For each dimension, if the length of items at // that depth is inconsistent, `undef` will be returned. If no items of that dimension depth exist, // `0` is returned. Otherwise, the consistent length of items in that dimensional depth is // returned. // Arguments: // v = Array to get dimensions of. // depth = Dimension to get size of. If not given, returns a list of dimension lengths. // Example: // a = array_dim([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]]); // Returns [2,2,3] // b = array_dim([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]], 0); // Returns 2 // c = array_dim([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]], 2); // Returns 3 // d = array_dim([[[1,2,3],[4,5,6]],[[7,8,9]]]); // Returns [2,undef,3] function array_dim(v, depth=undef) = assert( is_undef(depth) || ( is_finite(depth) && depth>=0 ), "Invalid depth.") ! is_list(v) ? 0 : (depth == undef) ? concat([len(v)], _array_dim_recurse(v)) : (depth == 0) ? len(v) : let( dimlist = _array_dim_recurse(v)) (depth > len(dimlist))? 0 : dimlist[depth-1] ; // Function: list_to_matrix() // Usage: // groups = list_to_matrix(v, [cnt], [dflt]); // Description: // Takes a flat array of values, and groups items in sets of `cnt` length. // The opposite of this is `flatten()`. // Topics: Matrices, Array Handling // See Also: column(), submatrix(), hstack(), flatten(), full_flatten() // Arguments: // v = The list of items to group. // cnt = The number of items to put in each grouping. Default:2 // dflt = The default value to fill in with if the list is not a multiple of `cnt` items long. Default: 0 // Example: // v = [1,2,3,4,5,6]; // a = list_to_matrix(v,2) returns [[1,2], [3,4], [5,6]] // b = list_to_matrix(v,3) returns [[1,2,3], [4,5,6]] // c = list_to_matrix(v,4,0) returns [[1,2,3,4], [5,6,0,0]] function list_to_matrix(v, cnt=2, dflt=0) = [for (i = [0:cnt:len(v)-1]) [for (j = [0:1:cnt-1]) default(v[i+j], dflt)]]; // Function: flatten() // Usage: // list = flatten(l); // Topics: Matrices, Array Handling // See Also: column(), submatrix(), hstack(), full_flatten() // Description: // Takes a list of lists and flattens it by one level. // Arguments: // l = List to flatten. // Example: // l = flatten([[1,2,3], [4,5,[6,7,8]]]); // returns [1,2,3,4,5,[6,7,8]] function flatten(l) = !is_list(l)? l : [for (a=l) if (is_list(a)) (each a) else a]; // Function: full_flatten() // Usage: // list = full_flatten(l); // Topics: Matrices, Array Handling // See Also: column(), submatrix(), hstack(), flatten() // Description: // Collects in a list all elements recursively found in any level of the given list. // The output list is ordered in depth first order. // Arguments: // l = List to flatten. // Example: // l = full_flatten([[1,2,3], [4,5,[6,7,8]]]); // returns [1,2,3,4,5,6,7,8] function full_flatten(l) = !is_list(l)? l : [for (a=l) if (is_list(a)) (each full_flatten(a)) else a]; // Function: zip() // Usage: // pairs = zip(a,b); // triples = zip(a,b,c); // quads = zip([LIST1,LIST2,LIST3,LIST4]); // Topics: List Handling, Iteration // See Also: zip_long() // Description: // Zips together two or more lists into a single list. For example, if you have two // lists [3,4,5], and [8,7,6], and zip them together, you get [ [3,8],[4,7],[5,6] ]. // The list returned will be as long as the shortest list passed to zip(). // Arguments: // a = The first list, or a list of lists if b and c are not given. // b = The second list, if given. // c = The third list, if given. // Example: // a = [9,8,7,6]; b = [1,2,3]; // for (p=zip(a,b)) echo(p); // // ECHO: [9,1] // // ECHO: [8,2] // // ECHO: [7,3] function zip(a,b,c) = b!=undef? zip([a,b,if (c!=undef) c]) : let(n = min_length(a)) [for (i=[0:1:n-1]) [for (x=a) x[i]]]; // Function: zip_long() // Usage: // pairs = zip_long(a,b); // triples = zip_long(a,b,c); // quads = zip_long([LIST1,LIST2,LIST3,LIST4]); // Topics: List Handling, Iteration // See Also: zip() // Description: // Zips together two or more lists into a single list. For example, if you have two // lists [3,4,5], and [8,7,6], and zip them together, you get [ [3,8],[4,7],[5,6] ]. // The list returned will be as long as the longest list passed to zip_long(), with // shorter lists padded by the value in `fill`. // Arguments: // a = The first list, or a list of lists if b and c are not given. // b = The second list, if given. // c = The third list, if given. // fill = The value to pad shorter lists with. Default: undef // Example: // a = [9,8,7,6]; b = [1,2,3]; // for (p=zip_long(a,b,fill=88)) echo(p); // // ECHO: [9,1] // // ECHO: [8,2] // // ECHO: [7,3] // // ECHO: [6,88]] function zip_long(a,b,c,fill) = b!=undef? zip_long([a,b,if (c!=undef) c],fill=fill) : let(n = max_length(a)) [for (i=[0:1:n-1]) [for (x=a) i