swiftでハノイの塔

swift playgrouds で2番目に書いたプログラム。

機能はパズル ハノイの塔 の手順を出力するだけ。

手順は以下のような、アニメーション(wikipediaより引用)ではなくて、単なる文字列です。

../../../_images/Tower_of_Hanoi_4.gif

作った関数

func hanoi(diskCount n: Int64)  -> [(pickUp: String, putDown: String)]

今回のメイン関数。円盤の枚数を与えると、手順をタプルのリストとして返す。 “pickUp” から “putDown” へ円盤を移動する。

func printHanoi01(hanoiArray: [(pickUp: String, putDown: String)])

関数 “hanoi” の表示をログに出力するための関数。

printHanoi01(hanoiArray: hanoi(diskCount:3))

上記のように評価すると、

(pickUp: "left", putDown: "right")
(pickUp: "left", putDown: "center")
(pickUp: "right", putDown: "center")
(pickUp: "left", putDown: "right")
(pickUp: "center", putDown: "left")
(pickUp: "center", putDown: "right")
(pickUp: "left", putDown: "right")

と出力される。

func hanoi(diskCount n: Int64)  -> [(pickUp: String, putDown: String)]

こちらは、出力を日本語にして見た。

同じく出力は以下のようになる。

円盤を 左側 から 右側 に移動する
円盤を 左側 から 中央 に移動する
円盤を 右側 から 中央 に移動する
円盤を 左側 から 右側 に移動する
円盤を 中央 から 左側 に移動する
円盤を 中央 から 右側 に移動する
円盤を 左側 から 右側 に移動する

コードを書いての感想

関数hanoiの中で、関数hanoi_internalを定義して使っている。Pascal風にローカル関数が書けるようになったのは便利。

hanoi本体は以下のような感じです。

// お皿の枚数を渡すと、ハノイの塔の手順を示す関数
func hanoi(diskCount n: Int64)  -> [(pickUp: String, putDown: String)]
{
    // 関数の中で関数定義ができる
    func hanoi_internal(diskCount n: Int64, from x:String, to y:String, work z:String) -> [(pickUp: String, putDown: String)]
    {
        if n != 0
        {
            return hanoi_internal( diskCount:n - 1, from:x, to:z, work:y)
                    + [(pickUp: x, putDown: y)]
                    + hanoi_internal( diskCount:n - 1, from:z, to:y, work:x)
        }
        else
        {
            return []
        }
    }

    return hanoi_internal(diskCount:n, from:"left", to:"right", work:"center")
}

hanoi_internalを無名関数にしてより簡潔に表記する方法がありそうだが見当たらなかった。

無名関数の中で自分自身へのポインターを示す特殊なキーワードがありそう。引数だと$0, $1, $2…などの表記があるので、$$がや$_あたりにあれば良いなと妄想中。

// こんな表記ができると最高なんだが...... $_を関数自身へのポインターとみなした場合
func hanoi(diskCount n: Int64)  -> [(pickUp: String, putDown: String)]
{
    return {
                diskCount n: Int64, from x:String, to y:String, work z:String)
                -> [(pickUp: String, putDown: String)] in

                if n != 0
                {
                    return $_( diskCount:n - 1, from:x, to:z, work:y)
                            + [(pickUp: x, putDown: y)]
                            + $_( diskCount:n - 1, from:z, to:y, work:x)
                }
                else
                {
                    return []
                }
    }(diskCount:n, from:"left", to:"right", work:"center")
}

参考文献とソースコード

プロジェクトファイル

  •  Logo 人工知能へのアプローチ
    https://www.amazon.co.jp/Logo-人工知能へのアプローチ-ラジオ技術選書-150-祐安-重夫/dp/4844301500