プログラミング言語 Standard ML 入門 (問題の解答例)
8 参照型

8.3 変更可能なデータ構造

問 8.6

dataDlistrightDlistleftDlistを定義せよ.

解答例 

   fun dataDlist (ref (CELL{data,...})) = data
   fun rightDlist (ref (CELL{right,...})) = right
   fun leftDlist (ref (CELL{left,...})) = left
問 8.7

deleteDlistfromListToDlist を定義せよ.

解答例 

   fun deleteDlist dlist =
       case dlist of
         ref NIL => ()
       | ref (CELL{left=l1 as ref (CELL{right=r2,left=l2,...}),
                   right=r1 as ref (CELL{right=r3,left=l3,...}),
                   ...}) =>
           if l1 = l2 then dlist := NIL
           else (dlist := !r1; r2 := !r1; l3 := !l1)

   fun fromListToDlist L = foldl (fn (x,y) => (insertDlist x y;y)) (ref NIL) (rev L)

補足: fromListToDlistは、リストの末尾から循環リストに追加してい く関数である。 foldrは末尾再帰の実装が困難なため、

   fun fromListToDlist L = foldr (fn (x,y) => (insertDlist x y;y)) (ref NIL) L

よりスタックオーバフローなどの危険も少なく効率がよい。
この観点から、リストを生成する関数、例えばtoListなども以 下のような実装が望ましい。

  fun toList L =
    let
      fun loop (l,A) visited =
        if member l visited then rev A
        else loop (rightDlist l, dataDlist l::A) (l::visited)
    in loop (rightDlist (leftDlist L), nil) nil
    end

さらに、循環リストは、リストの末尾からも辿ることができるので、 以下のより効率よい自然なコードが可能である。

  fun toList L =
    let
      fun loop (l,A) visited =
        if member l visited then A
        else loop (leftDlist l, dataDlist l::A) (l::visited)
    in loop (leftDlist L, nil) nil
    end

また、循環リストのリスト表現を逆順のリストとする定義すると、 以下の関数となる。

  fun toListL L =
    let
      fun loop (l,A) visited =
        if member l visited then A
        else loop (rightDlist l, dataDlist l::A) (l::visited)
    in loop (rightDlist (leftDlist L), nil) nil
    end

対応する循環リストへの変換は、

   fun fromListToDlistL L = foldl (fn (x,y) => (insert x y;y)) (ref NIL) L

問 8.8

2つの循環2重リストを連結する関数

   concatDlist : ’a dlist -> ’a dlsit -> unit

を定義せよ.ただし,連結の結果は,どちらのリストから見ても,セルを右にたど ると,以前のセルの後に他の循環2重リストのセルが付け加えられているようにせ よ.

解答例  以下にコード例を示す。

   fun concatDlist D1 D2 =
       case (D1, D2) of
         (ref NIL, _) => D1 := !D2
        | (_, ref NIL) => D2 := !D1
        | (ref (CELL{left=d1l as ref (CELL{right=d1lr,...}),...}),
           ref (CELL{left=d2l as ref (CELL{right=d2lr,...}),...})) =>
           let
             val d1lCell = !d1l
             val d1lrCell = !d1lr
           in
             (d1l := !d2l;
              d1lr := !d2lr;
              d2l := d1lCell;
              d2lr := d1lrCell)
           end
問 8.9

以下の関数を定義せよ.

  1. 1.

    循環2重リンクリストをコピーする関数 copyDlist

  2. 2.

    与えられた関数を循環2重リンクリストの各要素に適用して得られる値を要素 とする,新しい循環2重リンクリストを生成する関数 mapDlist

  3. 3.

    リストの foldr および foldl に相当する関数 foldrDlist および foldlDlist

解答例  これら関数の適切な定義には、以下の点を含む種々の注意深い考察が必要である。

  • 循環構造を辿る際の終了条件の適切な判定

  • leftおよびrightフィールドをたどると自分自身に戻ってくる CELLへの参照型データの作成

以下に、これら2点を考慮したコード例を示す。なお、copyDlistmapDlistとほぼ同様なコードであり、後者を使って定義している。

   fun mapDlist f d =
       let
         fun newElem x nil = NONE
           | newElem x ((h,newH)::t) =
             if x = h then SOME newH
             else newElem x t
         fun copy l copied =
             case l of
               ref NIL => ref NIL
             | ref (CELL{left, right, data}) =>
               (case newElem l copied of
                  NONE =>
                  let
                    val newL = ref NIL
                    val copied = (l, newL)::copied
                    val l = copy left copied
                    val r = copy right copied
                  in
                    (newL := CELL{left = l, right = r, data = f data};
                     newL)
                  end
                | SOME newL => newL
               )
       in
         copy (rightDlist (leftDlist d)) nil
       end

   fun copyDlist d = mapDlist (fn x => x) d

   fun foldrDlist F z d =
       let
         fun member x nil = false
           | member x (h::t) = x = h orelse member x t
         fun f d z visited =
             if member d visited then z
             else F (dataDlist d, f (rightDlist d) z (d::visited))
       in f (rightDlist (leftDlist d)) z nil
       end

   fun foldlDlist F z d =
       let
         fun member x nil = false
           | member x (h::t) = x = h orelse member x t
         fun f d z visited =
             if member d visited then z
             else f (rightDlist d) (F (dataDlist d, z)) (d::visited)
       in f (rightDlist (leftDlist d)) z nil
       end