From e3ccf482fadcc2e7f5a8d5021252d7136855b367 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Mon, 28 Sep 2020 19:12:07 -0700 Subject: [PATCH] Make list functions make more sense with strings. --- arrays.scad | 98 +++++++++++++++++++++++------------------- tests/test_arrays.scad | 4 +- version.scad | 2 +- 3 files changed, 57 insertions(+), 47 deletions(-) diff --git a/arrays.scad b/arrays.scad index 37fee58..b49cc85 100644 --- a/arrays.scad +++ b/arrays.scad @@ -199,7 +199,7 @@ function list_decreasing(list) = // Usage: // repeat(val, n) // Description: -// Generates a list or array of `n` copies of the given `list`. +// 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: @@ -259,14 +259,15 @@ function list_range(n=undef, s=0, e=undef, step=undef) = // Section: List Manipulation // Function: reverse() -// Description: Reverses a list/array. +// Description: Reverses a list/array or string. // Arguments: -// list = The list to reverse. +// x = The list or string to reverse. // Example: // reverse([3,4,5,6]); // Returns [6,5,4,3] -function reverse(list) = - assert(is_list(list)||is_string(list)) - [ for (i = [len(list)-1 : -1 : 0]) list[i] ]; +function reverse(x) = + assert(is_list(x)||is_string(x)) + let (elems = [ for (i = [len(x)-1 : -1 : 0]) x[i] ]) + is_string(x)? str_join(elems) : elems; // Function: list_rotate() @@ -275,6 +276,7 @@ function reverse(list) = // Description: // Rotates the contents of a list by `n` positions left. // If `n` is negative, then the rotation is `abs(n)` positions to the right. +// If `list` is a string, then a string is returned with the characters rotates within the string. // Arguments: // list = The list to rotate. // n = The number of positions to rotate by. If negative, rotated to the right. Positive rotates to the left. Default: 1 @@ -291,7 +293,8 @@ function reverse(list) = function list_rotate(list,n=1) = assert(is_list(list)||is_string(list), "Invalid list or string.") assert(is_finite(n), "Invalid number") - select(list,n,n+len(list)-1); + let (elems = select(list,n,n+len(list)-1)) + is_string(list)? str_join(elems) : elems; // Function: deduplicate() @@ -309,16 +312,18 @@ function list_rotate(list,n=1) = // Examples: // deduplicate([8,3,4,4,4,8,2,3,3,8,8]); // Returns: [8,3,4,8,2,3,8] // deduplicate(closed=true, [8,3,4,4,4,8,2,3,3,8,8]); // Returns: [8,3,4,8,2,3] -// deduplicate("Hello"); // Returns: ["H","e","l","o"] +// deduplicate("Hello"); // Returns: "Helo" // deduplicate([[3,4],[7,2],[7,1.99],[1,4]],eps=0.1); // Returns: [[3,4],[7,2],[1,4]] // deduplicate([[7,undef],[7,undef],[1,4],[1,4+1e-12]],eps=0); // Returns: [[7,undef],[1,4],[1,4+1e-12]] function deduplicate(list, closed=false, eps=EPSILON) = assert(is_list(list)||is_string(list)) - let( l = len(list), - end = l-(closed?0:1) ) - is_string(list) || (eps==0) - ? [for (i=[0:1:l-1]) if (i==end || list[i] != list[(i+1)%l]) list[i]] - : [for (i=[0:1:l-1]) if (i==end || !approx(list[i], list[(i+1)%l], eps)) list[i]]; + let( + l = len(list), + end = l-(closed?0:1) + ) + is_string(list) ? str_join([for (i=[0:1:l-1]) if (i==end || list[i] != list[(i+1)%l]) list[i]]) : + eps==0 ? [for (i=[0:1:l-1]) if (i==end || list[i] != list[(i+1)%l]) list[i]] : + [for (i=[0:1:l-1]) if (i==end || !approx(list[i], list[(i+1)%l], eps)) list[i]]; // Function: deduplicate_indexed() @@ -377,9 +382,9 @@ function deduplicate_indexed(list, indices, closed=false, eps=EPSILON) = // 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 // Examples: // list = [0,1,2,3]; -// echo(repeat_entries(list, 6)); // Ouputs [0,0,1,2,2,3] -// echo(repeat_entries(list, 6, exact=false)); // Ouputs [0,0,1,1,2,2,3,3] -// echo(repeat_entries(list, [1,1,2,1], exact=false)); // Ouputs [0,1,2,2,3] +// echo(repeat_entries(list, 6)); // Outputs [0,0,1,2,2,3] +// echo(repeat_entries(list, 6, exact=false)); // Outputs [0,0,1,1,2,2,3,3] +// echo(repeat_entries(list, [1,1,2,1], exact=false)); // Outputs [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)), @@ -414,7 +419,7 @@ function repeat_entries(list, N, exact = true) = // list_set([2,3,4,5], 2, 21); // Returns: [2,3,21,5] // 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_string(list)) + assert(is_list(list)) !is_list(indices)? ( (is_finite(indices) && indices=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]]; @@ -568,7 +574,7 @@ function list_bset(indexset, valuelist, dflt=0) = // Arguments: // array = A list of lists. function list_shortest(array) = - assert(is_list(array)||is_string(list), "Invalid input." ) + assert(is_list(array), "Invalid input." ) min([for (v = array) len(v)]); @@ -578,7 +584,7 @@ function list_shortest(array) = // Arguments: // array = A list of lists. function list_longest(array) = - assert(is_list(array)||is_string(list), "Invalid input." ) + assert(is_list(array), "Invalid input." ) max([for (v = array) len(v)]); @@ -590,7 +596,7 @@ function list_longest(array) = // minlen = The minimum length to pad the list to. // fill = The value to pad the list with. function list_pad(array, minlen, fill=undef) = - assert(is_list(array)||is_string(list), "Invalid input." ) + assert(is_list(array), "Invalid input." ) concat(array,repeat(fill,minlen-len(array))); @@ -601,7 +607,7 @@ function list_pad(array, minlen, fill=undef) = // array = A list. // minlen = The minimum length to pad the list to. function list_trim(array, maxlen) = - assert(is_list(array)||is_string(list), "Invalid input." ) + assert(is_list(array), "Invalid input." ) [for (i=[0:1:min(len(array),maxlen)-1]) array[i]]; @@ -614,7 +620,7 @@ function list_trim(array, maxlen) = // minlen = The minimum length to pad the list to. // fill = The value to pad the list with. function list_fit(array, length, fill) = - assert(is_list(array)||is_string(list), "Invalid input." ) + assert(is_list(array), "Invalid input." ) let(l=len(array)) l==length ? array : l> length ? list_trim(array,length) @@ -643,8 +649,10 @@ function _valid_idx(idx,imin,imax) = // Function: shuffle() // Description: // Shuffles the input list into random order. +// If given a string, shuffles the characters within the string. function shuffle(list) = assert(is_list(list)||is_string(list), "Invalid input." ) + is_string(list)? str_join(shuffle([for (x = list) x])) : len(list)<=1 ? list : let ( rval = rands(0,1,len(list)), @@ -763,6 +771,8 @@ function _indexed_sort(arrind) = // l3 = [[4,0],[7],[3,9],20,[4],[3,1],[8]]; // sorted3 = sort(l3); // Returns: [20,[3,1],[3,9],[4],[4,0],[7],[8]] function sort(list, idx=undef) = + assert(is_list(list)||is_string(list), "Invalid input." ) + is_string(list)? str_join(sort([for (x = list) x],idx)) : !is_list(list) || len(list)<=1 ? list : is_homogeneous(list,1) ? let(size = array_dim(list[0])) @@ -796,6 +806,7 @@ function sort(list, idx=undef) = // idxs2 = sortidx(lst, idx=0); // Returns: [1,2,0,3] // idxs3 = sortidx(lst, idx=[1,3]); // Returns: [3,0,2,1] function sortidx(list, idx=undef) = + assert(is_list(list)||is_string(list), "Invalid input." ) !is_list(list) || len(list)<=1 ? list : is_homogeneous(list,1) ? let( @@ -820,15 +831,16 @@ function sortidx(list, idx=undef) = // Function: unique() // Usage: -// unique(arr); +// l = unique(list); // Description: // Returns a sorted list with all repeated items removed. // Arguments: -// arr = The list to uniquify. -function unique(arr) = - assert(is_list(arr)||is_string(arr), "Invalid input." ) - len(arr)<=1? arr : - let( sorted = sort(arr)) +// list = The list to uniquify. +function unique(list) = + assert(is_list(list)||is_string(list), "Invalid input." ) + is_string(list)? str_join(unique([for (x = list) x])) : + len(list)<=1? list : + let( sorted = sort(list)) [ for (i=[0:1:len(sorted)-1]) if (i==0 || (sorted[i] != sorted[i-1])) sorted[i] @@ -837,18 +849,18 @@ function unique(arr) = // Function: unique_count() // Usage: -// unique_count(arr); +// counts = unique_count(list); // Description: -// Returns `[sorted,counts]` where `sorted` is a sorted list of the unique items in `arr` and `counts` is a list such -// that `count[i]` gives the number of times that `sorted[i]` appears in `arr`. +// Returns `[sorted,counts]` where `sorted` is a sorted list of the unique items in `list` and `counts` is a list such +// that `count[i]` gives the number of times that `sorted[i]` appears in `list`. // Arguments: -// arr = The list to analyze. -function unique_count(arr) = - assert(is_list(arr) || is_string(arr), "Invalid input." ) - arr == [] ? [[],[]] : - let( arr=sort(arr) ) - let( ind = [0, for(i=[1:1:len(arr)-1]) if (arr[i]!=arr[i-1]) i] ) - [ select(arr,ind), deltas( concat(ind,[len(arr)]) ) ]; +// list = The list to analyze. +function unique_count(list) = + assert(is_list(list) || is_string(list), "Invalid input." ) + list == [] ? [[],[]] : + let( list=sort(list) ) + let( ind = [0, for(i=[1:1:len(list)-1]) if (list[i]!=list[i-1]) i] ) + [ select(list,ind), deltas( concat(ind,[len(list)]) ) ]; // Section: List Iteration Helpers @@ -1132,7 +1144,7 @@ function subindex(M, idx) = // Function: submatrix() // Usage: submatrix(M, idx1, idx2) // Description: -// The input must be a list of lists (a matrix or 2d array). Returns a submatrix by selecting the rows listed in idx1 and columsn listed in idx2. +// The input must be a list of lists (a matrix or 2d array). Returns a submatrix by selecting the rows listed in idx1 and columns listed in idx2. // Arguments: // M = Given list of lists // idx1 = rows index list or range @@ -1336,8 +1348,6 @@ function array_dim(v, depth=undef) = -// This function may return undef! - // Function: transpose() // Usage: diff --git a/tests/test_arrays.scad b/tests/test_arrays.scad index 1dfe421..23ab265 100644 --- a/tests/test_arrays.scad +++ b/tests/test_arrays.scad @@ -112,7 +112,7 @@ test_list_range(); module test_reverse() { assert(reverse([3,4,5,6]) == [6,5,4,3]); - assert(reverse("abcd") == ["d","c","b","a"]); + assert(reverse("abcd") == "dcba"); assert(reverse([]) == []); } test_reverse(); @@ -136,7 +136,7 @@ test_list_rotate(); module test_deduplicate() { assert(deduplicate([8,3,4,4,4,8,2,3,3,8,8]) == [8,3,4,8,2,3,8]); assert(deduplicate(closed=true, [8,3,4,4,4,8,2,3,3,8,8]) == [8,3,4,8,2,3]); - assert(deduplicate("Hello") == ["H","e","l","o"]); + assert(deduplicate("Hello") == "Helo"); assert(deduplicate([[3,4],[7,1.99],[7,2],[1,4]],eps=0.1) == [[3,4],[7,2],[1,4]]); assert(deduplicate([], closed=true) == []); assert(deduplicate([[1,[1,[undef]]],[1,[1,[undef]]],[1,[2]],[1,[2,[0]]]])==[[1, [1,[undef]]],[1,[2]],[1,[2,[0]]]]); diff --git a/version.scad b/version.scad index f496c36..0e3bf96 100644 --- a/version.scad +++ b/version.scad @@ -8,7 +8,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,435]; +BOSL_VERSION = [2,0,436]; // Section: BOSL Library Version Functions