M.Hiroi's Home Page
http://www.geocities.jp/m_hiroi/

Functional Programming

Yet Another SML/NJ Problems

[ PrevPage | SML/NJ | NextPage ]

はじめに

今回はちょっと便利な関数を問題形式で紹介します。元ネタは P-99: Ninety-Nine Prolog Problems です。拙作のページ Prolog Programming Yet Another Prolog Problems と同じ問題ですが、あしからずご了承くださいませ。

●問題1

リストの要素がただひとつか調べる述語 single を定義してください。

val single = fn : 'a list -> bool
- single([]);
val it = false : bool
- single([1]);
val it = true : bool
- single([1, 2]);
val it = false : bool

解答

●問題2

リストの要素がひとつ以上あるか調べる述語 pair を定義してください。

val pair = fn : 'a list -> bool
- pair([]);
val it = false : bool
- pair([1]);
val it = true : bool
- pair([1, 2]);
val it = true : bool

解答

●問題3

リスト xs はリスト ys よりも長いか調べる述語 longer(xs, ys) を定義してください。

val longer = fn : 'a list * 'b list -> bool
- longer([1,2,3], [4,5,6]);
val it = false : bool
- longer([1,2,3], [4,5,6,7]);
val it = false : bool
- longer([1,2,3,4], [5,6,7]);
val it = true : bool
- longer([], [5,6,7]);
val it = false : bool
- longer([1,2,3,4], []);
val it = true : bool

解答

●問題4

リストの最後尾を求める関数 last_pair と、最後尾の要素を取り除く関数 butlast を定義してください。

exception Empty_list
val last_pair = fn : 'a list -> 'a list
val butlast = fn : 'a list -> 'a list
- last_pair([1,2,3,4]);
val it = [4] : int list
- last_pair([1]);
val it = [1] : int list

- butlast([1,2,3,4]);
val it = [1,2,3] : int list
- butlast([1]);
val it = [] : int list

解答

●問題5

リストの先頭から n 個の要素を取り出す関数 take(xs, n) を定義してください。

val take = fn : 'a list * int -> 'a list
- take([1,2,3,4,5], 3);
val it = [1,2,3] : int list
- take([1,2,3,4,5], 5);
val it = [1,2,3,4,5] : int list
- take([1,2,3,4,5], 0);
val it = [] : int list

解答

●問題6

リストの先頭から n 個の要素を取り除く関数 drop(xs, n) を定義してください。

val drop = fn : 'a list * int -> 'a list
- drop([1,2,3,4,5], 3);
val it = [4,5] : int list
- drop([1,2,3,4,5], 5);
val it = [] : int list
- drop([1,2,3,4,5], 0);
val it = [1,2,3,4,5] : int list

解答

●問題7

リストの n 番目から m - 1 番目の要素を部分リストとして取り出す関数 subseq xs n m を定義してください。なお、リストの要素は 0 から数え始めるものとします。

val subseq = fn : 'a list -> int -> int -> 'a list
- subseq [1,2,3,4,5,6] 1 4;
val it = [2,3,4] : int list
- subseq [1,2,3,4,5,6] 0 6;
val it = [1,2,3,4,5,6] : int list
- subseq [1,2,3,4,5,6] 2 3;
val it = [3] : int list
- subseq [1,2,3,4,5,6] 3 3;
val it = [] : int list

解答

●問題8

リストの末尾から n 個の要素を取り除く関数 butlastn(xs, n) を定義してください。

val butlastn = fn : 'a list * int -> 'a list
val it = [] : int list
- butlastn([1,2,3,4,5,6], 1);
val it = [1,2,3,4,5] : int list
- butlastn([1,2,3,4,5,6], 2);
val it = [1,2,3,4] : int list
- butlastn([1,2,3,4,5,6], 6);
val it = [] : int list
- butlastn([1,2,3,4,5,6], 0);
val it = [1,2,3,4,5,6] : int list

解答

●問題9

リスト xs を長さ n の部分リストに分割する述語 group(xs, n) を定義してください。

val group = fn : 'a list * int -> 'a list list
- group([1,2,3,4,5,6],2);
val it = [[1,2],[3,4],[5,6]] : int list list
- group([1,2,3,4,5,6],3);
val it = [[1,2,3],[4,5,6]] : int list list
- group([1,2,3,4,5,6],4);
val it = [[1,2,3,4],[5,6]] : int list list
- group([1,2,3,4,5,6],6);
val it = [[1,2,3,4,5,6]] : int list list

解答

●問題10

リスト xs の中から述語 pred が真を返す最初の要素を求める関数 find_if を定義してください。

val find_if = fn : ('a -> bool) -> 'a list -> 'a option
- find_if (fn(x) => x = 3) [1,2,3,4,5,6];
val it = SOME 3 : int option
- find_if (fn(x) => x = 30) [1,2,3,4,5,6];
val it = NONE : int option

解答

●問題11

リストの中から述語 pred が真を返す最初の要素の位置を求める関数 position_if を定義してください。なお、リストの要素は 0 から数え始めるものとします。

val position_if = fn : ('a -> bool) -> 'a list -> int option
- position_if (fn(x) => x = 3) [1,2,3,4,5,6];
val it = SOME 2 : int option
- position_if (fn(x) => x = 30) [1,2,3,4,5,6];
val it = NONE : int option

解答

●問題12

リストから述語 pred が真を返す要素の個数を求める関数 count_if を定義してください。

val count_if = fn : ('a -> bool) -> 'a list -> int
- count_if (fn(x) => x mod 2 = 0) [1,2,3,4,5,6,7];
val it = 3 : int
- count_if (fn(x) => x mod 2 = 1) [1,2,3,4,5,6,7];
val it = 4 : int

解答

●問題13

リストの要素の合計値を求める述語 sum_list を定義してください。

val sum_list = fn : int list -> int
- sum_list([]);
val it = 0 : int
- sum_list([1,2,3,4,5,6,7,8]);
val it = 36 : int

解答

●問題14

リストの中から最大値を求める関数 max_list と最小値を求める関数 min_list を定義してください。

val max_list = fn : int list -> int
val min_list = fn : int list -> int
- max_list([1,2,3,4,5,6,7,8]);
val it = 8 : int
- max_list([9,1,2,3,4,5,6,7,8]);
val it = 9 : int
- max_list([1,2,3,4,50,6,7,8]);
val it = 50 : int

- min_list([1,2,3,4,5,6,7,8]);
val it = 1 : int
- min_list([1,2,3,4,5,6,7,8,0]);
val it = 0 : int
- min_list([1,2,3,4,0,6,7,8]);
val it = 0 : int

解答

●問題15

要素 x の右隣に要素 y があるかチェックする関数 adjacent x y ls を定義してください。

val adjacent = fn : ''a * ''a * ''a list -> bool
- adjacent(1,2,[0,1,2,3,4,5]);
val it = true : bool
- adjacent(1,2,[0,1,0,2,3,4,5]);
val it = false : bool

解答

●問題16

整数 n から m までを格納したリストを作る関数 iota(n, m) を定義してください。

val iota = fn : int * int -> int list
- iota(0, 10);
val it = [0,1,2,3,4,5,6,7,8,9,10] : int list
- iota(10, 20);
val it = [10,11,12,13,14,15,16,17,18,19,20] : int list
- iota(1,0);
val it = [] : int list

解答

●問題17

リストから重複要素を取り除いて集合を生成する関数 set_of_list を定義してください。

val set_of_list = fn : ''a list -> ''a list
- set_of_list([1,2,3,1,2,3,4,1,2,3,4,5,6]);
val it = [1,2,3,4,5,6] : int list
- set_of_list([1,2,3,4,5]);
val it = [1,2,3,4,5] : int list

解答

●問題18

2 つの集合の和を求める関数 union を定義してください。

val union = fn : ''a list * ''a list -> ''a list
- union([1,2,3,4,5,6], [4,5,6,7,8,9]);
val it = [1,2,3,4,5,6,7,8,9] : int list
- union([1,2,3,4], [5,6,7,8]);
val it = [1,2,3,4,5,6,7,8] : int list
- union([1,2,3,4], [1,2,3,4]);
val it = [1,2,3,4] : int list

解答

●問題19

2 つの集合の積を求める関数 intersection を定義してください。

val intersection = fn : ''a list * ''a list -> ''a list
- intersection([1,2,3,4,5], [3,4,5,6,7]);
val it = [3,4,5] : int list
- intersection([1,2,3,4,5], [6,7,8,9,10]);
val it = [] : int list
- intersection([1,2,3,4,5], [1,2,3,4,5]);
val it = [1,2,3,4,5] : int list

解答

●問題20

2 つの集合の差を求める関数 difference を定義してください。

val difference = fn : ''a list * ''a list -> ''a list
- difference([1,2,3,4,5], [1,2,3,4,5]);
val it = [] : int list
- difference([1,2,3,4,5], [1,3,5]);
val it = [2,4] : int list
- difference([], [1,3,5]);
val it = [] : int list

解答

●問題21

2 つのソート済みのリストをひとつのソート済みのリストにまとめる関数 merge_list を定義してください。

val merge_list = fn : ('a * 'a -> bool) * 'a list * 'a list -> 'a list
- merge_list(op <, [1,3,5,7],[2,4,6,8]);
val it = [1,2,3,4,5,6,7,8] : int list
- merge_list(op <, [1,3,5,7],[0,2,4,6,8]);
val it = [0,1,2,3,4,5,6,7,8] : int list
- merge_list(op <, [0,1,3,5,7],[2,4,6,8]);
val it = [0,1,2,3,4,5,6,7,8] : int list

解答

●問題22

関数 merge_list を使ってリストをソートする merge_sort を定義してください。

val merge_sort = fn : ('a * 'a -> bool) * int * 'a list -> 'a list
- merge_sort(op <, 10, [5,6,4,7,3,8,2,9,1,0]);
val it = [0,1,2,3,4,5,6,7,8,9] : int list

解答

●問題23

リスト ps がリスト ls の「接頭辞 (prefix) 」か判定する述語 prefix(ls, ps) を定義してください。接頭辞とは、列の先頭からある位置までの部分列のことです。たとえば、リスト [1, 2, 3, 4] の接頭辞は [ ], [1], [1, 2], [1, 2, 3], [1, 2, 3, 4] の 5 つになります。

val prefix = fn : ''a list * ''a list -> bool
- prefix([1,2,3,4,5],[1,2,3]);
val it = true : bool
- prefix([1,2,3,4,5],[1,2]);
val it = true : bool
- prefix([1,2,3,4,5],[1]);
val it = true : bool
- prefix([1,2,3,4,5],[]);
val it = true : bool
- prefix([1,2,3,4,5],[3,4,5]);
val it = false : bool

解答

●問題24

リスト ss がリスト ls の「接尾辞 (suffix) 」か判定する述語 suffix(ls, ss) を定義してください。接尾辞とは、列のある位置から末尾までの部分列のことです。たとえば、リスト [1, 2, 3, 4] の接尾辞は [1, 2, 3, 4], [2, 3, 4], [3, 4], [4], [ ] の 5 つになります。

val suffix = fn : ''a list * ''a list -> bool
- suffix([1,2,3,4,5],[3,4,5]);
val it = true : bool
- suffix([1,2,3,4,5],[4,5]);
val it = true : bool
- suffix([1,2,3,4,5],[5]);
val it = true : bool
- suffix([1,2,3,4,5],[]);
val it = true : bool
- suffix([1,2,3,4,5],[3,4]);
val it = false : bool

解答

●問題25

リスト xs がリスト ls の部分リストか判定する述語 sublist(xs, ls) を定義してください。

val sublist = fn : ''a list * ''a list -> bool
- sublist([2,3,4],[1,2,3,4,5]);
val it = true : bool
- sublist([2,4],[1,2,3,4,5]);
val it = false : bool
- sublist([3,4,5],[1,2,3,4,5]);
val it = true : bool
- sublist([1,2,3],[1,2,3,4,5]);
val it = true : bool

解答


●解答1

リスト:要素がただひとつか

fun single([_]) = true
|   single(_) = false

SML/NJ の場合、引数のリストと [ _ ] がマッチングすれば、そのリストの要素は一つしかないことがわかります。length でリストの長さを求める必要はありません。

●解答2

リスト:要素がひとつ以上あるか

fun pair(_::_) = true
|   pair(_) = false

たとえば、リスト [1] と x::xs を照合すると、x = 1, xs = [ ] になります。したがって、引数のリストと _::_ がマッチングすれば、そのリストの要素は一つ以上あることがわかります。length でリストの長さを求める必要はありません。

なお、述語 pair の名前は Scheme の関数 pair? から拝借しました。

●解答3

リスト:リスト xs は ys よりも長いか

fun longer([], _) = false
|   longer(_, []) = true
|   longer(_::xs, _::ys) = longer(xs, ys)

リストの先頭から順番にたどり、途中で ys が空リストになれば xs の方が長いことがわかります。length でリストの長さを求めて比較するよりも、このプログラムの方が効率的だと思います。

●解答4

リスト: リストの最後尾を求める

exception Empty_list

fun last_pair([]) = raise Empty_list
|   last_pair([x]) = [x]
|   last_pair(_::xs) = last_pair(xs)
リスト:最後尾の要素を取り除く

fun butlast([]) = raise Empty_list
|   butlast([_]) = []
|   butlast(x::xs) = x :: butlast(xs)

どちらの関数も引数が空リストの場合はエラー Empty_list を送出します。last_pair は単純な再帰定義でリストの最後尾を求めています。butlast の 2 番目の節は、要素がひとつしかないリストから最後尾の要素を取り除くと空リストになることを表しています。これが再帰の停止条件になります。あとは次の節で butlast を再帰呼び出しして、xs から最後尾の要素を取り除いたリストに、引数のリストの先頭要素 x を追加していくだけです。

●解答5

リスト:リストの先頭から n 個の要素を取り出す

fun take(_, 0) = []
|   take([], n) = []
|   take(x::xs, n) = x :: take(xs, n - 1)

n が 0 の場合は空リストを返します。途中でリスト xs が空になった場合も空リストを返します。最後の節で take を再帰呼び出しして、その先頭に要素 x を追加します。

なお、SML/NJ の List モジュールには同等の機能を持つ関数 take が用意されています。

●解答6

リスト:リストの先頭から n 個の要素を削除する

fun drop(xs, 0) = xs
|   drop([], _) = []
|   drop(_::xs, n) = drop(xs, n - 1)

最初の節で、削除する要素数が 0 であればリスト xs をそのまま返します。次の節で、xs が空リストの場合は空リストを返します。最後の節で drop を再帰呼び出しして、xs から n - 1 個の要素を取り除いたリストを求めます。

なお、SML/NJ の List モジュールには同等の機能を持つ関数 drop が用意されています。

●解答7

リスト:部分リストを取り出す

fun subseq xs s e =
  if s > e then []
  else take(drop(xs, s), e - s)

subseq は drop と take を使うと簡単です。if 文で s と e の値をチェックして、s > e ならば空リストを返します。そうでなければ、drop で xs から s 個の要素を取り除き、そのリストから e - s 個の要素を take で取り出します。

●解答8

リスト:リストの末尾から n 個の要素を取り除く

fun butlastn(xs, n) = take(xs, length(xs) - n)

リスト xs の長さを m とすると、リストの末尾から n 個の要素を取り除くことは、リストの先頭から m - n 個の要素を取り出すことと同じになります。butlastn は取り出す要素の個数を計算して take で取り出すだけです。

●解答9

リスト:リストの分割

fun group([], _) = []
|   group(xs, n) = take(xs, n) :: group(drop(xs, n), n)

関数 group は take と drop を使うと簡単に定義できます。xs が空リストの場合は分割できないので空リストを返します。これが再帰の停止条件になります。xs が空リストでない場合、まず take で n 個の要素を格納したリストを求めます。次に、n 個の要素を取り除いたリストを drop で求め、group を再帰呼び出ししてそのリストを分割します。あとはその返り値に take で取り出したリストを追加するだけです。

●解答10

リスト : 述語が真となる要素を探索する

fun find_if _ [] = NONE
|   find_if pred (x::xs) = if pred x then SOME x else find_if pred xs

リストの先頭から順番に調べていき、pred の返り値が真であれば SOME x を返します。pred が真となる要素が見つからない場合は NONE を返します。なお、SML/NJ の List モジュールには同等の機能を持つ関数 find が用意されています。

●解答11

リスト:要素の位置を求める

fun position_if pred xs =
  let
    fun iter [] _ = NONE
    |   iter (x::xs) i = if pred x then SOME i else iter xs (i + 1)
  in
    iter xs 0
  end

局所関数 iter で要素の位置 i を求めます。リストの先頭から順番に調べていき、pred の返り値が真であれば SOME i を返します。pred が真となる要素が見つからない場合は NONE を返します。

●解答12

リスト:要素の個数を求める

fun count_if pred xs =
  let
    fun iter [] a = a
    |   iter (x::xs) a = if pred x then iter xs (a + 1) else iter xs a
  in
    iter xs 0
  end

(* 別解 *)
fun count_if1 pred xs =
  foldl (fn(x, a) => if pred x then a + 1 else a) 0 xs

局所関数 iter で要素の個数をカウントします。引数 a を累積変数として使います。pred x が真の場合、a を +1 して iter を再帰呼び出しします。そうでなければ a の値をそのままにして iter を再帰呼び出しします。リストが空リストの場合は a を返します。別解は畳み込みを行う関数 foldl を使って書き直したものです。

●解答13

リスト:要素の合計値を求める

fun sum_list xs =
  let
    fun iter [] a = a
    |   iter (x::xs) a = iter xs (a + x)
  in
    iter xs 0
  end

(* 別解 *)
fun sum_list1 xs = foldl (fn(x, a) => x + a) 0 xs

局所関数 iter で要素の合計値を求めます。引数 a を累積変数として使っていて、iter を再帰呼び出しするとき、a に x を加算します。リストが空リストの場合は a を返します。別解は foldl を使って書き直したものです。

●解答14

リスト:リストから最大値と最小値を求める

fun max_list [] = raise Empty_list
|   max_list (x::xs) =
  let
    fun iter [] a = a
    |   iter (x::xs) a = if x > a then iter xs x else iter xs a
  in
    iter xs x
  end

fun min_list [] = raise Empty_list
|   min_list (x::xs) =
  let
    fun iter [] a = a
    |   iter (x::xs) a = if x < a then iter xs x else iter xs a
  in
    iter xs x
  end

(* 別解 *)
fun max_list1 [] = raise Empty_list
|   max_list1 (x::xs) =
  List.foldl (fn(b, a) => if b > a then b else a) x xs

fun min_list1 [] = raise Empty_list
|   min_list1 (x::xs) =
  List.foldl (fn(b, a) => if b < a then b else a) x xs

どちらの関数も局所変数 iter で最大値 (最小値) を求めます。引数 a を累積変数として使っていて、そこに最大値 (または最小値) を保持します。最初に呼び出すとき、リストの先頭要素をセットします。あとは残りの要素を順番に調べていき、リストの先頭要素 x が a よりも大きい (または小さい) 場合は、それを a に置き換えるだけです。別解は foldl で書き直したものです。

ところで、このままでは int list だけにしか適用することができません。他のデータ型にも適用したい場合は、次のように述語を渡して高階関数にするとよいでしょう。

リスト : 高階関数バージョン

fun max_list2 _ [] = raise Empty_list
|   max_list2 pred (x::xs) =
  foldl (fn(b, a) => if pred(b, a) then b else a) x xs
val max_list2 = fn : ('a * 'a -> bool) -> 'a list -> 'a

簡単な実行例を示します。

- max_list2 (op >) [1.1, 2.2, 3.3, 6.6, 5.5, 4.4];
val it = 6.6 : real
- max_list2 (op >) ["abc", "def", "ghi", "ABC"];
val it = "ghi" : string

- val max_real_list : real list -> real = max_list2 (op >);
val max_real_list = fn : real list -> real
- max_real_list [1.1, 2.2, 3.3, 6.6, 5.5, 4.4];
val it = 6.6 : real

- val max_string_list : string list -> string = max_list2 (op >);
val max_string_list = fn : string list -> string
- max_string_list ["abc", "def", "ghi", "ABC"];
val it = "ghi" : string

●解答15

リスト:a と b は隣り合っているか

fun adjacent(_, _, []) = false
|   adjacent(_, _, [_]) = false
|   adjacent(a, b, x::y::ys) =
  if x = a andalso y = b then true
  else adjacent(a, b, y::ys)

関数 adjacent の定義は簡単です。リストが x::y::ys とマッチングして、x = a かつ y = b を満たせば、a と b は隣り合っていることがわかります。そうでなければ、adjacent を再帰呼び出しして残りのリスト y::ys から探します。

●解答16

リスト:数列の生成

fun iota(n, m) =
  let
    fun iter i a =
      if i < n then a else iter (i - 1) (i::a)
  in
    iter m []
  end

局所関数 iter でリストを生成します。累積変数 a にリストを保持します。後ろ (m) から数値を生成してリストに格納していくことに注意してください。i < n になったら a を返します。

●解答17

リスト:集合の生成

fun mem(_, []) = false
|   mem(x, y::ys) = if x = y then true else mem(x, ys)

fun set_of_list xs =
  let
    fun iter [] a = rev a
    |   iter (y::ys) a =
           if mem(y, ys) then iter ys a else iter ys (y::a)
  in
    iter xs []
  end

関数 set_of_list はリストから重複要素を取り除きます。実際の処理は局所関数 iter で行います。リストの先頭要素 y が残りのリスト ys に含まれているか関数 mem でチェックします。同じ要素がない場合は累積変数 a に y を追加します。

●解答18

リスト:集合の和

fun union([], ys) = ys
|   union(x::xs, ys) =
      if mem(x, ys) then union(xs, ys)
      else x :: union(xs, ys)

(* 別解 *)
fun union1(xs, ys) =
  foldl (fn(x, a) => if mem(x, ys) then a else x::a) ys xs

xs が空リストの場合は ys を返します。これは空集合 (空リスト) と集合 ys の和は ys であることを表しています。次の節でリストを x::xs に分解して、x が ys に含まれていなければ、x を集合に追加します。含まれている場合は集合に追加しません。別解は foldl で書き直したものです。

●解答19

リスト:集合の積

fun intersection([], _) = []
|   intersection(x::xs, ys) =
      if mem(x, ys) then x :: intersection(xs, ys)
      else intersection(xs, ys)

(* 別解 *)
fun intersection1(xs, ys) =
  foldl (fn(x, a) => if mem(x, ys) then x::a else a) [] xs

xs が空リストの場合は空リストを返します。これは空集合 (空リスト) と集合 ys の積は空リストであることを表しています。次の節でリストを x::xs に分解して、x が ys に含まれていれば x を集合に追加します。含まれていない場合は集合に追加しません。別解は foldl で書き直したものです。

●解答20

リスト:集合の差

fun difference([], _) = []
|   difference(x::xs, ys) =
      if mem(x, ys) then difference(xs, ys)
      else x :: difference(xs, ys)

(* 別解 *)
fun difference1(xs, ys) =
  foldl (fn(x, a) => if mem(x, ys) then a else x::a) [] xs

xs が空リストの場合は空リストを返します。これは空集合 (空リスト) と集合 ys の差は空リストであることを表しています。次の節でリストを x::xs に分解して、x が ys に含まれていなければ x を集合に追加します。含まれている場合は集合に追加しません。別解は foldl で書き直したものです。

●解答21

リスト:リストのマージ

fun merge_list(_, [], ys) = ys
|   merge_list(_, xs, []) = xs
|   merge_list(pred, x as x1::xs, y as y1::ys) =
      if pred(x1, y1) then x1 :: merge_list(pred, xs, y)
      else y1 :: merge_list(pred, x, ys)

要素の比較は述語 pred で行います。xs が空リストの場合は ys を返し、ys が空リストの場合は xs を返します。次に、x と y の先頭要素 x1 と y1 を pred で比較します。pred(x1, y1) が真の場合は x1 をリストに追加します。そうでなければ y1 をリストに追加します。

●解答22

リスト:マージソート

fun merge_sort(_, _, []) = []
|   merge_sort(_, 1, x::_) = [x]
|   merge_sort(pred, 2, x1::x2::_) =
  if pred(x1, x2) then [x1, x2] else [x2, x1]
|   merge_sort(pred, n, xs) =
  let
    val m = n div 2
  in
    merge_list(pred, merge_sort(pred, m, xs), merge_sort(pred, n - m, drop(xs, m)))
  end

要素の比較は述語 pred で行います。引数 n はリスト xs の長さを表します。要素が一つしかない場合は [x] を返します。2 つある場合は要素 x1 と x2 を pred で比較し、pred(x1, x2) が真であれば [x1, x2] を、そうでなければ [x2, x1] を返します。それ以外の場合は、リスト xs を二分割して merge_sort を再帰呼び出しし、その結果を merge_list でマージします。

●解答23

リスト:接頭辞の判定

fun prefix(_, []) = true
|   prefix([], _) = false
|   prefix(x::xs, y::ys) = if x = y then prefix(xs, ys) else false

接頭辞の判定は簡単です。最初の節は、空リストは接頭辞であることを表しています。次の節で ls が空リストの場合、ks は接頭辞ではないので false を返します。それ以外の場合は、ls と ks の先頭要素を比較して、等しい場合は prefix を再帰呼び出しして次の要素を比較します。等しくない場合は接頭辞ではないので false を返します。

●解答24

リスト:接尾辞の判定

fun suffix(ls, ks) =
  let
    val n1 = List.length(ls)
    val n2 = List.length(ks)
  in
    ks = drop(ls, n1 - n2)
  end

接尾辞の判定も簡単です。リスト ls と ks の長さを求め、ls の先頭から (n1 - n2) 個の要素を取り除きます。これで ls と ks の長さが等しくなるので、あとは単純に演算子 = で比較するだけです。

●解答25

リスト:部分リストの判定

fun sublist(ks, ls) =
  if prefix(ls, ks) then true
  else if ls = [] then false
  else sublist(ks, tl(ls))

sublist は prefix を使うと簡単です。最初の if で ks が ls の接頭辞であれば部分リストなので true を返します。ls が空リストの場合、ks は部分リストではないので false を返します。それ以外の場合は ls の先頭要素を取り除いて、sublist を再帰呼び出しするだけです。


Copyright (C) 2012 Makoto Hiroi
All rights reserved.

[ PrevPage | SML/NJ | NextPage ]