OpenSCAD 用户手册/列表推导
[注意: 需要版本 2015.03]
列表推导提供了一种灵活的方式来使用通用语法生成列表
[ list-definition expression ]
以下元素支持构建列表定义
- for (i = sequence)
- 遍历范围或现有列表
- for (init;condition;next)
- 以 C 样式 for 表示的简单递归调用
- each
- 接受一个序列值作为参数,并将每个元素添加到正在构建的列表中。each x 等效于 `for (i = x) i`
- if (condition)
- 选择标准,为真时计算表达式并将其添加到结果列表中
- let (x = value)
- 局部变量赋值
[注意: 需要版本 2019.05]
列表推导语法已推广以允许多个表达式。这允许轻松地从多个由不同列表推导表达式生成的子列表构建列表,避免了 concat。
steps = 50;
points = [
// first expression generating the points in the positive Y quadrant
for (a = [0 : steps]) [ a, 10 * sin(a * 360 / steps) + 10 ],
// second expression generating the points in the negative Y quadrant
for (a = [steps : -1 : 0]) [ a, 10 * cos(a * 360 / steps) - 20 ],
// additional list of fixed points
[ 10, -3 ], [ 3, 0 ], [ 10, 3 ]
];
polygon(points);
for 元素定义列表生成的输入值。语法与 for 迭代器使用的语法相同。等号右侧的序列可以是任何列表。for 元素遍历列表的所有成员。等号左侧的变量依次取序列中每个成员的值。然后可以在 for 元素的子级中处理此值,并且每个结果都成为生成的最终列表的成员。
如果序列有多个维度,for 只遍历第一个维度。可以通过嵌套 for 元素访问更深的维度。
这里介绍了一些常见的用法模式。
- [ for (i = [start : step : end]) i ]
- 根据范围定义生成输出,此版本主要用于计算列表值或使用范围值作为索引访问现有列表。
示例
// generate a list with all values defined by a range
list1 = [ for (i = [0 : 2 : 10]) i ];
echo(list1); // ECHO: [0, 2, 4, 6, 8, 10]
// extract every second character of a string
str = "SomeText";
list2 = [ for (i = [0 : 2 : len(str) - 1]) str[i] ];
echo(list2); // ECHO: ["S", "m", "T", "x"]
// indexed list access, using function to map input values to output values
function func(x) = x < 1 ? 0 : x + func(x - 1);
input = [1, 3, 5, 8];
output = [for (a = [ 0 : len(input) - 1 ]) func(input[a]) ];
echo(output); // ECHO: [1, 6, 15, 36]
- [ for (i = [a, b, c, ...]) i ]
- 使用列表参数作为输入,此版本可用于将输入值映射到计算的输出值。
示例
// iterate over an existing list
friends = ["John", "Mary", "Alice", "Bob"];
list = [ for (i = friends) len(i)];
echo(list); // ECHO: [4, 4, 5, 3]
// map input list to output list
list = [ for (i = [2, 3, 5, 7, 11]) i * i ];
echo(list); // ECHO: [4, 9, 25, 49, 121]
// calculate Fibonacci numbers
function func(x) = x < 3 ? 1 : func(x - 1) + func(x - 2);
input = [7, 10, 12];
output = [for (a = input) func(a) ];
echo(output); // ECHO: [13, 55, 144]
- [ for (c = "String") c ]
- 根据字符串生成输出,这将遍历字符串的每个字符。
[注意: 需要版本 2019.05]
示例
echo([ for (c = "String") c ]);
// ECHO: ["S", "t", "r", "i", "n", "g"]
- [ for (a = inita, b = initb, ...;condition;a = nexta, b = nextb, ...) expr ]
- 用于以 c 样式 for 循环表达简单递归调用的生成器。
[注意: 需要版本 2019.05]
此生成器的递归等效项为
function f(a, b, ...) = condition ? concat([expr], f(nexta, nextb, ...)) : []; f(inita, initb, ...)
示例
echo( [for (a = 0, b = 1;a < 5;a = a + 1, b = b + 2) [ a, b * b ] ] );
// ECHO: [[0, 1], [1, 9], [2, 25], [3, 49], [4, 81]]
// Generate fibonacci sequence
echo([for (a = 0, b = 1;a < 1000;x = a + b, a = b, b = x) a]);
// ECHO: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987]
// Cumulative sum of values in v
function cumsum(v) = [for (a = v[0]-v[0], i = 0; i < len(v); a = a+v[i], i = i+1) a+v[i]];
echo(cumsum([1, 2, 3, 4]));
// ECHO: [1, 3, 6, 10]
echo(cumsum([[1, 1], [2, 2], [3, 3]]));
// ECHO: [[1, 1], [3, 3], [6, 6]]
[注意: 需要版本 2019.05]
each 直接嵌入作为参数给出的列表的值,有效地解包参数列表。
// Without using "each", a nested list is generated
echo([ for (a = [1 : 4]) [a, a * a] ]);
// ECHO: [[1, 1], [2, 4], [3, 9], [4, 16]]
// Adding "each" unwraps the inner list, producing a flat list as result
echo([ for (a = [1 : 4]) each [a, a * a] ]);
// ECHO: [1, 1, 2, 4, 3, 9, 4, 16]
each 解包范围并帮助在与多个生成器表达式结合使用时构建更通用的 for 列表。
A = [-2, each [1:2:5], each [6:-2:0], -1];
echo(A);
// ECHO: [-2, 1, 3, 5, 6, 4, 2, 0, -1]
echo([ for (a = A) 2 * a ]);
// ECHO: [-4, 2, 6, 10, 12, 8, 4, 0, -2]
if 元素允许选择是否应分配表达式并将其添加到结果列表中。在最简单的情况下,这允许过滤列表。
- [ for (i = list) if (condition(i)) i ]
- 当条件的计算结果为真时,表达式 i 将被添加到结果列表中。
示例
list = [ for (a = [ 1 : 8 ]) if (a % 2 == 0) a ];
echo(list); // ECHO: [2, 4, 6, 8]
请注意,if 元素不能在表达式内,它应该在顶部。
示例
// from the input list include all positive odd numbers
// and also all even number divided by 2
list = [-10:5];
echo([for(n=list) if(n%2==0 || n>=0) n%2==0 ? n/2 : n ]);
// ECHO: [-5, -4, -3, -2, -1, 0, 1, 1, 3, 2, 5]
// echo([for(n=list) n%2==0 ? n/2 : if(n>=0) n ]); // this would generate a syntactical error
[注意: 需要版本 2019.05]
if-else 结构等效于条件表达式 ?:,只是它可以与 filter if 组合使用。
- [ for (i = list) if (condition(i)) x else y ]
- 当条件的计算结果为真时,表达式 x 将被添加到结果列表中,否则表达式 y 将被添加到结果列表中。
// even numbers are halved, positive odd numbers are preserved, negative odd numbers are eliminated
echo([for (a = [-3:5]) if (a % 2 == 0) [a, a/2] else if (a > 0) [a, a] ]);
// ECHO: [[-2, -1], [0, 0], [1, 1], [2, 1], [3, 3], [4, 2], [5, 5]];
请注意,在上面的表达式中,条件运算符不能替代 if-else。可以使用条件运算符来表达这种相同的过滤器,但逻辑更隐晦。
// even numbers are halved, positive odd numbers are preserved, negative odd numbers are eliminated
echo([for (a = [-3:5]) if (a % 2 == 0 || (a % 2 != 0 && a > 0)) a % 2 == 0 ? [a, a / 2] : [a, a] ]);
// ECHO: [[-2, -1], [0, 0], [1, 1], [2, 1], [3, 3], [4, 2], [5, 5]];
要将 else 表达式绑定到特定的 if,可以使用括号。
// even numbers are dropped, multiples of 4 are substituted by -1
echo([for(i=[0:10]) if(i%2==0) (if(i%4==0) -1 ) else i]);
// ECHO: [-1, 1, 3, -1, 5, 7, -1, 9]
// odd numbers are dropped, multiples of 4 are substituted by -1
echo([for(i=[0:10]) if(i%2==0) if(i%4==0) -1 else i]);
// ECHO: [-1, 2, -1, 6, -1, 10]
let 元素允许在列表推导定义内按顺序赋值变量。
- [ for (i = list) let (assignments) a ]
示例
list = [ for (a = [ 1 : 4 ]) let (b = a*a, c = 2 * b) [ a, b, c ] ];
echo(list); // ECHO: [[1, 1, 2], [2, 4, 8], [3, 9, 18], [4, 16, 32]]
有不同的方法来定义嵌套循环。在一个 for 元素中定义多个循环变量和多个 for 元素都会产生扁平的结果列表。要生成嵌套的结果列表,需要额外的 [ ] 标记。
// nested loop using multiple variables
flat_result1 = [ for (a = [ 0 : 2 ], b = [ 0 : 2 ]) a == b ? 1 : 0 ];
echo(flat_result1); // ECHO: [1, 0, 0, 0, 1, 0, 0, 0, 1]
// nested loop using multiple for elements
flat_result2 = [ for (a = [ 0 : 2 ]) for (b = [0 : 2]) a == b ? 1 : 0 ];
echo(flat_result2); // ECHO: [1, 0, 0, 0, 1, 0, 0, 0, 1]
// nested loop to generate a bi-dimensional matrix
identity_matrix = [ for (a = [ 0 : 2 ]) [ for (b = [ 0 : 2 ]) a == b ? 1 : 0 ] ];
echo(identity_matrix); // ECHO: [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
本章列出了一些列表推导语法的进阶示例、常用习语和用例。
使用列表推导,可以在许多点上计算参数方程以近似许多曲线,例如以下椭圆示例(使用 polygon())
sma = 20; // semi-minor axis
smb = 30; // semi-major axis
polygon(
[ for (a = [0 : 5 : 359]) [ sma * sin(a), smb * cos(a) ] ]
);
列表推导可用于用户定义的函数中以对向量执行任务或操作。这是一个将嵌套向量扁平化的用户定义函数。
// input : nested list
// output : list with the outer level nesting removed
function flatten(l) = [ for (a = l) for (b = a) b ] ;
nested_list = [ [ 1, 2, 3 ], [ 4, 5, 6 ] ];
echo(flatten(nested_list)); // ECHO: [1, 2, 3, 4, 5, 6]
即使是复杂的算法 快速排序 也可以使用 for()、if()、let() 和 递归 来实现
// input : list of numbers
// output : sorted list of numbers
function quicksort(arr) = !(len(arr)>0) ? [] : let(
pivot = arr[floor(len(arr)/2)],
lesser = [ for (y = arr) if (y < pivot) y ],
equal = [ for (y = arr) if (y == pivot) y ],
greater = [ for (y = arr) if (y > pivot) y ]
) concat(
quicksort(lesser), equal, quicksort(greater)
);
// use seed in rands() to get reproducible results
unsorted = [for (a = rands(0, 10, 6, 3)) ceil(a)];
echo(unsorted); // ECHO: [6, 1, 8, 9, 3, 2]
echo(quicksort(unsorted)); // ECHO: [1, 2, 3, 6, 8, 9]
select() 用于选择和重新排列元素到一个新的向量中。
function select(vector, indices) = [ for (index = indices) vector[index] ];
vector1 = [[0,0],[1,1],[2,2],[3,3],[4,4]];
selector1 = [4,0,3];
vector2 = select(vector1,selector1); // [[4, 4], [0, 0], [3, 3]]
vector3 = select(vector1,[0,2,4,4,2,0]);// [[0, 0], [2, 2], [4, 4],[4, 4], [2, 2], [0, 0]]
// range also works as indices
vector4 = select(vector1, [4:-1:0]); // [[4, 4], [3, 3], [2, 2], [1, 1], [0, 0]]
使用索引
function cat(L1, L2) = [for (i=[0:len(L1)+len(L2)-1])
i < len(L1)? L1[i] : L2[i-len(L1)]] ;
echo(cat([1,2,3],[4,5])); //concatenates two OpenSCAD lists [1,2,3] and [4,5], giving [1, 2, 3, 4, 5]
不使用索引
function cat(L1, L2) = [for(L=[L1, L2], a=L) a];
echo(cat([1,2,3],[4,5])); //concatenates two OpenSCAD lists [1,2,3] and [4,5], giving [1, 2, 3, 4, 5]