小舟に乗って川を渡る #(人 狼 羊 菜) たち

Smalltalker's Salon Mailing List (SML) の 青木 淳さんの投稿より。

小舟に乗って川を渡ろうとしている人がいる。この人は狼と羊と菜を持っている。しかし、川を渡るための小舟には、一回に持って渡れるものがたった一つという制限がある。狼を持って渡ろうとすると、渡っている間に羊が菜を食べてしまう。菜を持って渡ろうとすると、渡っている間に狼が羊を食べてしまう。すべてのものを安全に対岸に渡すためのダイナミックモデル (状態遷移図) を作成せよ。


ここで、部分集合とその補集合について、その可否の判断の結果を出力するスクリプトSqueakSmalltalk で書いてみました。さっそく、ファウラーの「コレクションクロージャメソッド」にからめて作ったメモを活用。w 可否の判断には #noneSatisfy: を、組み合わせには #combinations:atATimeDo: を使ってみます。

| 構成 無効 可否出力 補集合 可否 |

構成 := #(人 狼 羊 菜).
無効 := #((狼 羊) (羊 菜) (狼 羊 菜)).

World findATranscript: nil.

可否出力 := [: 集合 |
   補集合 := 構成 reject: [: 要素 | 集合 includes: 要素].
   可否 := {集合. 補集合} noneSatisfy: [: 各々 | 無効 includes: 各々].

   Transcript cr; show: 集合 printString.
   Transcript show: ' - '; show: 補集合 printString.
   Transcript show: ' -> '; show: 可否 printString].

可否出力 value: #().

(1 to: 構成 size) do: [: nn |
   構成 combinations: nn atATimeDo: [: 組み合わせ | 可否出力 value: 組み合わせ]]

なお、#combinations:atATimeDo: は第一引数に 0(抽出なし)を想定していないようなので、出力作業をブロックにして括りだし、ループの外で別途、空集合時の出力を得ました。


このスクリプトを do it したときの、トランスクリプトSmalltalk システムの標準出力もどき)への出力はこんなかんじ。

#() - #(#人 #狼 #羊 #菜) -> true
#(#人) - #(#狼 #羊 #菜) -> false
#(#狼) - #(#人 #羊 #菜) -> true
#(#羊) - #(#人 #狼 #菜) -> true
#(#菜) - #(#人 #狼 #羊) -> true
#(#人 #狼) - #(#羊 #菜) -> false
#(#人 #羊) - #(#狼 #菜) -> true
#(#人 #菜) - #(#狼 #羊) -> false
#(#狼 #羊) - #(#人 #菜) -> false
#(#狼 #菜) - #(#人 #羊) -> true
#(#羊 #菜) - #(#人 #狼) -> false
#(#人 #狼 #羊) - #(#菜) -> true
#(#人 #狼 #菜) - #(#羊) -> true
#(#人 #羊 #菜) - #(#狼) -> true
#(#狼 #羊 #菜) - #(#人) -> false
#(#人 #狼 #羊 #菜) - #() -> true


Nihongo7、および、日本語用リソースを追加した Squeak 3.8 で動作します。