関数的なリストは、コンストラクトした後で不変な単純な片方向リストです。 次のデータ型宣言は、関数的なリストを表わす型 list を導入しています:
// datatype list(a:t@ype, int) = | list_nil(a, 0) of () | {n:nat} list_cons(a, n+1) of (a, list(a, n)) //
ATSLIB にはリストを生成する様々な関数があります。 実際には、コンストラクタ list_nil と list_cons を直接使ってリストは生成されます。 例えば次の関数 fromto は、与えられた2つの整数の範囲にある整数群を持つリストを生成します:
// fun fromto {m,n:int | m <= n} ( m: int(m), n: int(n) ) : list(int, n-m) = if m < n then list_cons(m, fromto(m+1, n)) else list_nil() //
// fun {a:t@ype} list_length {n:nat} ( xs: list(a, n) ) : int(n) = let // fun loop {i,j:nat} ( xs: list(a, i), j: int(j) ) : int(i+j) = ( case+ xs of | list_nil () => j | list_cons (_, xs) => loop(xs, j+1) ) // in loop (xs, 0) end // end of [list_length] //
// fun {a,b:t@ype} list_foldleft {n:nat} ( f: (a, b) -> a, ini: a, xs: list(b, n) ) : a = if iseqz(xs) then ini else list_foldleft(f, f(ini, xs.head), xs.tail) // end of [if] //
// fun {a,b:t@ype} list_foldright {n:nat} ( f: (a, b) -> b, xs: list(a, n), snk: b ) : b = if iseqz(xs) then snk else f(xs.head, list_foldright(f, xs.tail, snk)) // end of [if] //
list_foldleft の適用例として、次のコードは与えられたリストを逆順にする関数を実装しています:
fun {a:t@ype} list_reverse ( xs: List0(a) ) : List0(a) = ( list_foldleft<List0(a),a> (lam (xs, x) => list_cons(x, xs), list_nil, xs) )
fun {a:t@ype} list_reverse {n:nat} ( xs: list(a, n) ) : list(a, n) = let // fun loop{i,j:nat} ( xs: list(a, i), ys: list(a, j) ) : list(a, i+j) = case+ xs of | list_nil () => ys | list_cons (x, xs) => loop (xs, list_cons (x, ys)) // in loop (xs, list_nil) end // end of [list_reverse]
list_foldright の適用例として、次のコードは与えられた2つのリストを連結する関数を実装しています:
// fun {a:t@ype} list_append ( xs: List0(a), ys: List0(a) ) : List0(a) = list_foldright<a, List0(a)>(lam (x, xs) => list_cons(x, xs), ys, xs) //
// fun {a:t@ype} list_append {m,n:nat} ( xs: list(a,m), ys: list(a,n) ) : list(a,m+n) = ( case+ xs of | list_nil () => ys | list_cons (x, xs) => list_cons (x, list_append (xs, ys)) ) //
読者は関数的なリストを、リストの長さより小さい自然数 i からリストの i 番目の要素をマップする連想配列として考えるかもしれません。 次の関数 list_get_at は、与えられた位置のリスト要素へのアクセスを表わしています:
// fun {a:t@ype} list_get_at {n:nat} ( xs: list(a, n), i: natLt(n) ) : a = if i > 0 then list_get_at(xs.tail, i-1) else xs.head //
不変であるため、関数的なリストに対する破壊的な更新はできません。 次の関数 list_set_at は呼び出されると、与えられた位置にある要素を与えられた要素で置き換えたリストを生成します:
// fun {a:t@ype} list_set_at {n:nat} ( xs: list(a, n), i: natLt(n), x0: a ) : list(a, n) = if i > 0 then list_cons(xs.head, list_set_at(xs.tail, i-1, x0)) else list_cons(x0, xs.tail) // end of [if] //
関数プログラミングにおいて、関数的なリストは最も広く使われるデータ構造です。 けれども、関数的なリストを配列のように使うべきではありません。 それは時間効率と空間効率の両面で効果的ではありません。
この章で使ったコード全体とテストコードは オンライン から入手できます。