1 00:00:00,600 --> 00:00:01,640 みなさん、こんにちは。 2 00:00:01,800 --> 00:00:04,400 今日は、私も含め、Pharo 開発者が 3 00:00:04,560 --> 00:00:08,360 よくやってしまう間違いについて お話します。 4 00:00:08,520 --> 00:00:12,520 よくある間違いをより早く見つけて 修正する方法をお見せします。 5 00:00:13,560 --> 00:00:16,280 ここにコードがあります。 6 00:00:16,440 --> 00:00:20,000 実行すると、デバッガーが開いて 7 00:00:21,080 --> 00:00:24,880 あるオブジェクトに self メッセージが送られ 8 00:00:25,040 --> 00:00:27,680 そのオブジェクトが self メッセージを 理解できない、と言ってきます。 9 00:00:27,840 --> 00:00:32,400 self はメッセージではないと言いたいのですが 実際、とてもよく送られます。 10 00:00:32,560 --> 00:00:36,120 コードのどこかに間違いがあるのです。 11 00:00:36,280 --> 00:00:38,680 ちょっと見てみると 12 00:00:39,840 --> 00:00:41,800 ピリオドが抜けていることに気付きます。 13 00:00:42,760 --> 00:00:45,800 そのために実行中に 14 00:00:45,960 --> 00:00:50,360 self が DiceHandle new の結果に 送られてしまいます。 15 00:00:50,520 --> 00:00:53,640 DiceHandle クラスには self メソッドはないので 16 00:00:53,800 --> 00:00:56,000 デバッガーが開きます。 17 00:00:57,560 --> 00:01:02,240 修正するには、最初の行の最後に ピリオドを追加すれば良いです。 18 00:01:03,720 --> 00:01:06,600 よくあるもう 1 つの問題は 19 00:01:06,760 --> 00:01:11,920 繋がらないはずのメッセージが 繋がってしまっている場合です。 20 00:01:12,080 --> 00:01:16,280 ここで「includes:ifTrue: は存在しない」 というエラーになります。 21 00:01:16,440 --> 00:01:19,400 includes: も ifTrue: も存在しますが includes:IfTrue: は存在しません。 22 00:01:19,560 --> 00:01:21,560 よく見ると 23 00:01:21,880 --> 00:01:24,720 本当に includes:ifTrue: メッセージが 24 00:01:24,880 --> 00:01:27,240 送られている、という事実に気付きます。 25 00:01:27,720 --> 00:01:32,120 レシーバー x に引数が 2 つ。 33 と ブロックです。 26 00:01:32,320 --> 00:01:34,320 動きません。 27 00:01:34,480 --> 00:01:36,600 Pharo がキーワードを見る時には 28 00:01:36,760 --> 00:01:39,120 それに続く全てのキーワードを見ようとします。 29 00:01:39,280 --> 00:01:41,880 全てのキーワードを取り込んで それを 1 つのメッセージと見做します。 30 00:01:42,040 --> 00:01:44,160 つまり、括弧が 31 00:01:44,320 --> 00:01:45,920 欠けていたのです。 32 00:01:46,080 --> 00:01:49,480 括弧があれば ifTrue: メッセージが x includes: 33 の結果に送られます。 33 00:01:50,520 --> 00:01:54,440 同様に assert:includes: なんてありません。 34 00:01:54,600 --> 00:01:57,760 意図しているのは includes: の結果を assert: することなので 35 00:01:57,920 --> 00:02:01,440 括弧が必要です。 36 00:02:02,040 --> 00:02:05,680 1 つの式の中で 複数のキーワードメッセージを使う時は 37 00:02:05,880 --> 00:02:09,280 躊躇せずに括弧を入れて 38 00:02:09,440 --> 00:02:13,200 キーワードメッセージを分離してください。 さもなくば Pharo は 1 つのメッセージとして 39 00:02:13,360 --> 00:02:16,160 くっつけてしまいます。 40 00:02:16,400 --> 00:02:18,600 この例では 41 00:02:19,080 --> 00:02:23,320 numbers に数値のコレクションが欲しいのですが 42 00:02:23,480 --> 00:02:27,720 今のところ数はたった 1 つ 35 しかありません。 43 00:02:27,880 --> 00:02:33,000 しかし、numbers の値を見ると コレクションではなく 44 00:02:33,160 --> 00:02:35,840 35 という数値になっています。 おかしいですね。 45 00:02:37,200 --> 00:02:41,240 同様に、このコードでは 46 00:02:41,400 --> 00:02:45,120 Dice クラスに new メッセージを送ると 47 00:02:45,280 --> 00:02:47,400 整数 6 が得られます。 48 00:02:47,600 --> 00:02:49,200 6 面のダイスではなく。 49 00:02:49,600 --> 00:02:52,200 どちらの問題も、原因は同じです。 50 00:02:52,360 --> 00:02:54,360 もっとしっかり見てみると 51 00:02:54,520 --> 00:02:57,880 add: の後に yourself を追加すると 52 00:02:58,040 --> 00:02:59,520 問題が解決します。 なぜかというと 53 00:02:59,720 --> 00:03:02,280 add: は加えたオブジェクトを返すからです。 54 00:03:03,000 --> 00:03:05,560 つまり、 OrderedCollection new add: 35 55 00:03:05,720 --> 00:03:07,920 は、35 を返します。 56 00:03:08,080 --> 00:03:12,000 最後に yourself を追加すれば 確実にレシーバーが得られます。 57 00:03:12,160 --> 00:03:14,200 そうすれば numbers は 数値のコレクションになるはずです。 58 00:03:14,840 --> 00:03:15,960 つまりこの場合の解決策は 59 00:03:16,160 --> 00:03:20,640 各メッセージの最後に yourself を追加することです。 60 00:03:21,560 --> 00:03:22,680 また別の問題があります。 61 00:03:22,880 --> 00:03:26,000 Book クラスの borrow メソッドがあります。 62 00:03:26,160 --> 00:03:29,200 実行すると 63 00:03:29,360 --> 00:03:32,040 nil does not understand ifFalse: というエラーメッセージが出ます。 64 00:03:32,200 --> 00:03:35,960 つまり、ここでは ifFalse: メッセージを nil に送ってしまっています。 65 00:03:36,120 --> 00:03:38,440 どういうことかというと inLibrary の値は nil つまり 66 00:03:38,600 --> 00:03:41,360 変数のデフォルト値のままということです。 67 00:03:41,520 --> 00:03:46,360 おそらくは、inLibrary は 初期化されていないのでしょう。 68 00:03:46,520 --> 00:03:51,440 この変数にデフォルト値を設定してあげる 必要があります。 69 00:03:51,600 --> 00:03:53,960 修正はとても簡単です。 70 00:03:54,120 --> 00:03:56,240 initialize メソッドで 71 00:03:56,400 --> 00:04:01,880 Book クラスのインスタンスを 生成するごとに 72 00:04:02,040 --> 00:04:04,480 インスタンス変数 inLibrary に 73 00:04:04,640 --> 00:04:07,280 True を入れればよいのです。 74 00:04:07,640 --> 00:04:09,680 ただし、このコードを今実行すると 75 00:04:09,880 --> 00:04:12,280 別のエラーメッセージが出ます。 76 00:04:12,440 --> 00:04:16,800 Class True does not understand ifFalse: 77 00:04:17,520 --> 00:04:21,200 原因はどこでしょう? 答えは、ここで代入したものが 78 00:04:21,360 --> 00:04:23,040 クラスだからです。 79 00:04:23,200 --> 00:04:25,400 真偽値ではなくクラスなのです。 80 00:04:26,040 --> 00:04:29,360 真偽値の真の値は true です。 小文字で始まります。 81 00:04:30,480 --> 00:04:34,880 クラス名は一般に大文字で始まります。 82 00:04:35,040 --> 00:04:37,280 つまり True はクラスで 83 00:04:37,440 --> 00:04:41,240 true は True クラスの 唯一のインスタンスです。 84 00:04:42,000 --> 00:04:43,920 また別の問題があります。 85 00:04:44,240 --> 00:04:46,840 Dice クラスの roll メソッドでは 86 00:04:47,000 --> 00:04:49,760 ダイスを振ると 87 00:04:49,920 --> 00:04:52,520 1 から面の数までの数が得られます。 88 00:04:52,680 --> 00:04:56,280 ただし、この場合は ダイスを振るとダイスが得られてしまいます。 89 00:04:56,440 --> 00:05:00,200 出た目ではなく。 90 00:05:01,200 --> 00:05:05,520 お見せしたメソッドは 下のほうのメソッドと等価です。 91 00:05:05,800 --> 00:05:10,760 つまりリターンのないメソッドは デフォルトで self を返すのです。 92 00:05:11,680 --> 00:05:15,680 そのため roll メソッドが実行されると 振った結果として 93 00:05:15,840 --> 00:05:18,360 ダイス自身を返します。 94 00:05:20,040 --> 00:05:24,120 faces コレクションに atRandom を 送った結果ではなく。 95 00:05:24,920 --> 00:05:27,360 同じ原因ですがちょっと違う例です。 96 00:05:27,800 --> 00:05:31,200 Dice クラスに新しいメソッドを 97 00:05:31,360 --> 00:05:33,400 定義しています。 98 00:05:33,560 --> 00:05:35,080 Dice クラスに 99 00:05:35,240 --> 00:05:39,400 Dice クラスのインスタンスを生成する 新しいメソッドを追加したいのですが 100 00:05:39,560 --> 00:05:43,120 デフォルトの面の数を ゼロに初期化しています。 101 00:05:44,120 --> 00:05:46,880 Dice クラスに new メッセージを送ると 102 00:05:47,040 --> 00:05:49,480 Dice クラス自身が返ってきます。 103 00:05:49,640 --> 00:05:52,120 Dice クラスの 新しいインスタンスではなく。 104 00:05:53,040 --> 00:05:54,080 どちらの場合でも 105 00:05:54,280 --> 00:05:56,760 リターンがないために 106 00:05:56,920 --> 00:06:01,240 デフォルトで self つまり レシーバーを返しています。 107 00:06:01,400 --> 00:06:02,760 クラスメソッドの場合 self はクラスです。 108 00:06:03,200 --> 00:06:04,920 これらの問題を解決するには 109 00:06:05,560 --> 00:06:09,520 返したい値を返すために キャレットを追加するだけです。 110 00:06:11,200 --> 00:06:12,160 次の問題は 111 00:06:12,600 --> 00:06:15,480 このコードが実行されると 112 00:06:15,640 --> 00:06:20,520 システムが固まってしまって 何もできなくなります。 113 00:06:20,680 --> 00:06:23,360 Pharo と対話することが できなくなります。 114 00:06:23,960 --> 00:06:25,120 何が原因でしょう? 115 00:06:25,440 --> 00:06:29,000 Dice クラスに新しいメソッドを 116 00:06:29,160 --> 00:06:31,920 定義していますが 117 00:06:33,200 --> 00:06:36,280 self は Dice クラスで 118 00:06:36,440 --> 00:06:40,960 つまり self new は 自分自身を再帰的に呼び出しています。 119 00:06:41,320 --> 00:06:42,680 ここで意図していることは 120 00:06:43,280 --> 00:06:46,640 Dice クラスのスーパークラスの デフォルトのインスタンス生成を 121 00:06:46,800 --> 00:06:48,560 利用することで 122 00:06:48,720 --> 00:06:51,400 それに何か処理を加えたいのです。 123 00:06:51,560 --> 00:06:53,920 このように書くと 無限ループになります。 124 00:06:54,640 --> 00:06:57,680 なので、ここでは self の代わりに super と書き換える必要があります。 125 00:06:57,840 --> 00:07:01,720 すると スーパークラスでの実装を 使うことができます。 126 00:07:03,000 --> 00:07:04,880 理解していただきたいことは 127 00:07:06,520 --> 00:07:08,360 我々は皆、たくさんの間違いをおかします。 128 00:07:08,520 --> 00:07:11,600 今お見せした間違いは 全ての Pharo 開発者が 129 00:07:11,760 --> 00:07:13,800 とても頻繁にやってしまうものです。 130 00:07:13,960 --> 00:07:17,880 ありがちなのは 131 00:07:18,040 --> 00:07:20,360 ピリオドの抜け 132 00:07:20,520 --> 00:07:22,240 括弧の抜け 133 00:07:22,400 --> 00:07:24,480 キャレットの抜け 134 00:07:24,640 --> 00:07:26,160 そして yourself の抜けです。 135 00:07:26,840 --> 00:07:29,800 問題の根本を見つけるために 136 00:07:30,000 --> 00:07:33,280 デバッガーを使ってみてください。 137 00:07:33,440 --> 00:07:36,040 すぐに閉じたりしないでください。 138 00:07:36,200 --> 00:07:39,520 すぐに閉じてしまうと、問題を修正する 道を見失ってしまいます。