函式實字
March 4, 2022OpenSCAD 本身沒有提供排序用的函式,如果你想針對數字排序該怎麼做呢?來實作個〈快速排序法〉吧!
function sort(lt) =
let(leng = len(lt))
leng <= 1 ? lt :
let(
pivot = lt[0],
before = [for(j = 1; j < leng; j = j + 1) if(lt[j] < pivot) lt[j]],
after = [for(j = 1; j < leng; j = j + 1) if(lt[j] >= pivot) lt[j]]
)
[each sort(before), pivot, each sort(after)];
// ECHO: [1, 2, 3, 5, 6, 7, 8, 10]
echo(sort([3, 1, 2, 5, 7, 6, 10, 8]));
嗯?有說要由小而大排序嗎?另外,如果想針對其他類似的資料排序,例如字串長短排序呢?
建立函式值
如果想要實作有彈性的 sort
函式,那麼就不能寫死 lt[j] < pivot
、lt[j] >= pivot
的部份,最好是可以指定函式,例如:
function sort(lt, compare) =
let(leng = len(lt))
leng <= 1 ? lt :
let(
pivot = lt[0],
before = [for(j = 1; j < leng; j = j + 1) if(compare(lt[j], pivot) < 0) lt[j]],
after = [for(j = 1; j < leng; j = j + 1) if(compare(lt[j], pivot) >= 0) lt[j]]
)
[each sort(before, compare), pivot, each sort(after, compare)];
這邊規範 compare
傳回結果若大於 0,表示第一個引數在順序上大於第二個引數,若等於 0,表示順序相同,若小於 0,表示第一個引數在順序上小於第二個引數。
那麼該怎麼傳入函式呢?就目前為止你看到的函式定義,不能作為值傳遞給 sort
,如果想要一個可以作為值傳遞的函式,可以使用函式實字(function literal),例如 function(a, b) a - b
是個函式實字,要看仔細,函式實字沒有名稱,參數列後沒有 =
,直接就是函式本體。
在程式語言中,實字意謂著你可以用書寫的方式建立一個值,例如,3.14 是個數字實字,會建立一個數值,"abc"
是個字串實字,會建立一個字串值,function(a, b) a - b
是個函式實字的話,表示它會建立一個函式值。
既然是值,就可以指定給變數:
compare = function(a, b) a - b;
或者裝在 list 中成為元素:
compare_functions = [
function(a, b) a - b,
function(a, b) b - a
];
也可以作為引數傳遞:
// ECHO: [10, 8, 7, 6, 5, 3, 2, 1]
echo(
sort(
[3, 1, 2, 5, 7, 6, 10, 8],
compare = function(a, b) b - a
)
);
你想針對字串長度排序也可以了:
echo(
sort(
["a", "aa", "a", "aaa"],
compare = function(a, b) len(a) - len(b)
)
);
function vs function?
OpenSCAD 的函式實字,是在 2021.01 版本加入的特性,很可惜地,它使用了與函式定義相同的關鍵字 function
,例如,以下是個 ascending
函式定義:
function ascending(a, b) = a - b;
以下建立了一個函式實字,指定給 descending
變數:
descending = function(a, b) b - a;
OpenSCAD 的函式實字,其實就相當於其他語言中的 lambda 運算式(lambda expression)、匿名函式(anonymous function)、一級函式(first class function)之類的特性,如果能採用 (a, b) -> a - b
或者 lambda(a, b) a - b
之類語法,就不會與函式定義混淆,無論如何,既然函式實字也是使用 function
關鍵字,習慣就好…XD
在〈OpenSCAD CheatSheet〉裡的函式,都是函式定義,函式定義不能作為值傳遞(模組也不行),然而,可以在函式實字中呼叫 OpenSCAD 內建的函式定義,例如,function(lt) len(lt)
是函式實字,就可以傳遞了,像是傳遞給 leng
變數:
leng = function(lt) len(lt);
函式實字沒有名稱,不過若最後指定給變數,還是可以透過變數名稱來進行遞迴,例如遞迴地計算階乘:
factorial = function(n) n == 1 ? 1 : n * factorial(n - 1);
// ECHO: 120
echo(factorial(5));
既然函式實字建立了函式值,想作為參數預設值也是可以的,例如預設可以比較數字,採昇幕排序:
function sort(lt, compare = function(a, b) a - b) =
let(leng = len(lt))
leng <= 1 ? lt :
let(
pivot = lt[0],
before = [for(j = 1; j < leng; j = j + 1) if(compare(lt[j], pivot) < 0) lt[j]],
after = [for(j = 1; j < leng; j = j + 1) if(compare(lt[j], pivot) >= 0) lt[j]]
)
[each sort(before, compare), pivot, each sort(after, compare)];
// ECHO: [1, 2, 3, 5, 6, 7, 8, 10]
echo(sort([3, 1, 2, 5, 7, 6, 10, 8]));