高階関数

高階関数とは

関数を引数に取るものまたは関数を返すものを、高階関数または汎関数といいます。

ここで紹介する高階関数は、リストと関数を引数にとり、リストの各要素に関数の適用を行います。しかも、引数の関数を場合によって差し替えることで、柔軟な処理を行えます。手続き型言語でループとして記述する多くの場面が、これらの高階関数で置き換えることが可能です。逆に言うと、関数型言語の高階関数は、手続き型言語のループで記述できます。要は、どちらが記述しやすく、可読性があり、後からの変更を行いやすいかという問題になります。LISP では、リストでデータを保持するのが自然な局面が多く、リストと高階関数は大変相性が良いので、LISP においては積極的に高階関数を使用することをお勧めします。

なお、ここで引数のリストはドットリストを指定できません。指定した場合は、エラーが発生します。

ソート

以下のリストのソート関数は、リストの順位を決定する比較関数を引数で指定する必要があります。比較関数は、次のように二つの引数を受け取り、nil かnil 以外を返す関数にします。

(defun comparison-function (item1 item2)
    (item1 とitem2 を比較して nil か nil 以外を返す)
)

どのような比較で nil とするかはプログラミング次第ですが、nil の場合に順番の入れ替えが起こります。

AutoLISP関数
(vl-sort list 'comparison-function)
list:リスト
comparison-function:比較関数
指定された比較関数にしたがってリスト内の要素をソートします。
戻り値:リスト

vl-sort 関数は、リストの要素のソートを行います。

list 引数は、ソートするリストを指定します。

comparison-function 引数には、比較関数を指定します。

なお、他の高階関数の引数が、「関数、リスト」の並びになっていますが、ソートのものだけ「リスト、関数」の並びになっているので注意してください。

戻り値はソート済みのリストが返りますが、引数のリストのコピーであり元のリストには影響を与えません。

簡単な使い方は以下のとおりです。「<」は一文字ですが組み込みの関数名で、引数で渡された要素の「大なり」の判定を行います。vl-sort 関数は、重複した要素が省略されることがあるので、注意が必要です。

_$ (vl-sort '(1 3 5 4 5 2 3) '<)⏎
(1 2 3 4 5)

次は、文字列を含んだリストで、文字列の長さが短い順に並べ替えます。

_$ (vl-sort '("apple" "pineapple" "orenge" "berry") '(lambda (i1 i2) (< (strlen i1) (strlen i2)))) ⏎
("apple" "berry" "orenge" "pineapple")

次は、数値を含んだリストのリストで、各リストの合計が小さい順に並べ替えます。

_$ (vl-sort '((1 2 3) (3 4) (2 1 2)) '(lambda (i1 i2) (< (apply '+ i1) (apply '+ i2)))) ⏎
((2 1 2) (1 2 3) (3 4))
AutoLISP関数
(vl-sort-i list 'comparison-function)
list:リスト
comparison-function:比較関数
指定された比較関数にしたがってリスト内の要素をソートし、それに対応したもとのリスト要素のインデックス番号のリストで結果を返します。
戻り値:リスト

vl-sort-i 関数も vl-sort 関数と同じく、リストのソートを行い、引数の使用法も同じです。

しかし、戻り値のリストは、list 引数の要素のインデックスで表されます。例えば、もともとのリスト要素でインデックスが 3 だったものが、ソートの結果、一番初めになった場合は、戻り値のリストの先頭にインデックスの整数 3 が入ります。インデックス番号は 0 から始まることに注意してください。

簡単な使い方は以下のとおりです。例では、引数のリストは整数データのリストですが、戻り値のリストはインデックスのリストです。vl-sort 関数は、重複した要素が省略されることがありましたが、vl-sort-i 関数では省略はおこりません。

_$ (vl-sort-i '(1 3 5 4 5 2 3) '<) ⏎
(0 5 6 1 3 4 2)

比較した結果で同順だった場合、vl-sort 関数とvl-sort-i 関数で、どちらを先にするかは異なる結果になる場合があります。次の例は、文字列の長さでソートしています。"apple" と "berry" の長さが同じですが、二つの関数で結果の順番が異なっています。

_$ (vl-sort '("apple" "pineapple" "orenge" "berry") '(lambda (i1 i2) (if (< (strlen i1) (strlen i2)) T))) ⏎
("apple" "berry" "orenge" "pineapple")
_$ (vl-sort-i '("apple" "pineapple" "orenge" "berry") '(lambda (i1 i2) (if (< (strlen i1) (strlen i2)) T))) ⏎
(3 0 2 1)

次は、数値を含んだリストのリストで、各リストの合計が小さい順に並べ替えます。

_$ (vl-sort-i '((1 2 3) (3 4) (2 1 2)) '(lambda (i1 i2) (< (apply '+ i1) (apply '+ i2)))) ⏎
(2 0 1)

検索

以下のリストの検索関数は、どの要素を検索するのかを決定する判定関数を引数で指定する必要があります。判定関数は、次のように一つの引数を受け取り、nil か nil 以外を返す関数にします。

(defun predicate-function (item)
    (item を検査してnil かnil 以外を返す)
)

どういうケースでnil とするかはプログラミング次第です。

AutoLISP関数
(vl-member-if 'predicate-function list)
predicate-function:テスト関数
list:リスト
あるリストの要素に対して、テスト関数が nil 以外の値を返した場合、それ以降のリストを返します。
戻り値:リスト

vl-member-if 関数は、predicate-function 関数が nil 以外を返す要素が見つかったら、その要素以降のリストを返します。number 関数は vl-number-if 関数の特殊な形式と言えます。

list 引数は検査するリストを指定します。

戻り値のリストは、引数で渡したリストのコピーで、返すリストが無いときは nil を返します。

下は 3 で割り切れる要素を検索し、それ以降のリストを返します。

_$ (vl-member-if '(lambda (num) (zerop (rem num 3))) '(1 2 3 4 5 6)) ⏎
(3 4 5 6)
AutoLISP関数
(vl-member-if-not 'predicate-function list)
predicate-function:テスト関数
list:リスト
あるリストの要素に対して、テスト関数が nil を返した場合、それ以降のリストを返します。
戻り値:リスト

vl-member-if-not 関数は、vl-number-if 関数の反対に predicate-function 関数が nil を返した要素以降のリストを返します。

list 引数は検査するリストを指定します。

戻り値のリストは、引数で渡したリストのコピーで、返すリストが無いときは nil を返します。

下は数値以外の要素が見つかったら、それ以降のリストを返します。

_$ (vl-member-if-not '(lambda (a) (numberp a)) '(1 2 "A" 3 4 5 6)) ⏎
("A" 3 4 5 6)