vieweditattachhistoryswikistopchangessearchhelp

As I was going to St. Ives

結城さんの日記、「2004年2月16日 (月) - セント・アイヴズへ行く道すがら」から、

……「そう、42億9496万7296通りのものを表現できます。 2のn乗は、プログラマにとってはfamiliarな「切りの良い」数ですね。 ここからが問題です」

彼女は突然、英語で歌を歌いだした。美しいソプラノだ。

As I was going to St. Ives
    (セント・アイヴズへ行く道すがら)
I met a man with seven wives,
    (7人の奥さんを連れた男に会った)
Each wife had seven sacks,
    (奥さんはそれぞれ7つの袋を持ち)
Each sack had seven cats,
    (袋にはそれぞれ7匹の猫がいて)
Each cat had seven kits:
    (猫にはそれぞれ7匹の子猫がいた)
Kits, cats, sacks, and wives,
    (子猫と猫と袋と奥さん)
How many were there going to St. Ives?
    (セント・アイヴズへ行ったのは全部でどれだけ?)

歌い終わると彼女は私の答えを待たず、 すぐに姿を消してしまった。

というクイズ。またもや、趣旨をくみ取れず…(^_^;)。ちなみに、この直前までは 2 の累乗に関するやりとりが少しあります。これがヒントなのか、そうでないのか…。ということで、よく分からないので、解答が示されるまでまた、Smalltalk(Squeak) システムで遊んでみましょう。とりあえず、歌の内容を Smalltalk のコードに書き下ろしてみると、

| man |
man ← Owner new.
(man had: 7 wives) do: [ : eachWife | 
	(eachWife had: 7 sacks) do: [ : eachSack | 
		(eachSack had: 7 cats) do: [ : eachCat | 
			(eachCat had: 7 kits) ]]].
↑ man howManyCompanions

=> 2801

となり、出会ったものの合計の数は 2801 であることが分かります。したがって、答えは、
  1. この歌の主人公を含め、出会った男一行もろともセントアイブスに行き着いたのなら、
  2. この歌の主人公を含めないなら、
  3. 最後から二行目をコンテキストに入れる、つまり、子猫と猫と袋と女房だけで男は含めないなら、
  4. 2行目の with をたんに「女房もちの」と解釈し、その場には連れていないとするなら主人公を含めて、
  5. 上で主人公を含めないか、男は結局、セントアイブスに行かなかった場合、主人公を数えて、
  6. 上で主人公を数えなければ、
のいずれか。--sumim

とりようによっては、人間だけ、それを広げて生き物だけ、という解釈も可能ですね。そこでクラス構成と Owner >> #howManyCompanions にちょっと細工をして条件付きで数えられるようにしてみましょう。

| man |
man ← Human new.
(man had: 7 wives) do: [ : eachWife | 
	(eachWife had: 7 sacks) do: [ : eachSack | 
		(eachSack had: 7 cats) do: [ : eachCat | 
			(eachCat had: 7 kits) ]]].
↑ man howManyCompanions: [ : each | each isKindOf: Human ]

=> 8


| man |
man ← Human new.
(man had: 7 wives) do: [ : eachWife | 
	(eachWife had: 7 sacks) do: [ : eachSack | 
		(eachSack had: 7 cats) do: [ : eachCat | 
			(eachCat had: 7 kits) ]]].
↑ man howManyCompanions: [ : each | each isKindOf: Mammal ]

=> 2752
--sumim

解説

Smalltalk に馴染みのない人のために、どうしてこんな文章みたいなコードが動くのか、ちょっと(不親切な(^_^;))解説を加えておきましょう。

前半の叙述的な部分のポイントは、OrderedCollection を継承した Owner 、Mammal 、Human というクラスと、#wives 、#sacks 、#cats 、#kits という an Integer (クラス Integer のインスタンスのこと。以下同じ) に対するメッセージの存在です。

Owner には、Owner >> #had: を定義していますが、これは OrderedCollection >> #addAll: (OrderedCollection クラスに定義された #addAll: メソッドの意味。以下同じ) 、つまり、パラメータのコレクションの要素を自身に加える、というメソッドに無条件委譲しているので両者は等価です。

Integer >> #wives 、#sacks 、#cats 、#kits は、レシーバと同数の an Owner (あるいは、a Mammal 、a Human ) を要素にとる an Array を生成するしくみを提供しています。

たとえば、man had: 7 wives は、まず、7 に wives を送ることで、a Human (実体は an OrdredCollection )を7つ含んだ an Array を作り、それを man に束縛された a Human に had: を介して addAll: しています。

こうしてできた、a Human の各要素に対して、do: [ : each | "each に束縛された各要素に対する処理" ] を介して要素の追加(wife なら sack = an Owner、sack なら cat = a Mammal、cat なら kit = a Mammal を要素として追加する)という手続きをネストして行ないます。結果、入れ子状態の巨大な a kind of OrdredCollection が出来あがります。

最後の Owner >> #howManyCompanions: (あるいは #howManyCompanions )は、各要素の #howManyCompanions: を再帰的に合計したものに1を足して返すものです。#howManyCompanions: は判定用のブロッククロージャ(無名関数)をパラメータにとり、1を足すか足さないか(自分を数えるか、数えないか)の判断に用いています。Human は Mammal のサブクラスなので、an Human は isKindOf: Human にも isKindOf: Mammal というメッセージに対しても true を返します。


コメントはこちらへ

この歌、映画『ダイハード3』のネタとして使われているらしいです。(私は見たけど覚えていません…) --- seeds



このページを編集 (4760 bytes)


Congratulations! 以下の 1 ページから参照されています。

This page has been visited 5423 times.