跳转到内容

OpenSCAD 用户手册/列表推导

来自 Wikibooks,开放世界的开放书籍

[注意: 需要版本 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]
华夏公益教科书