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]