どうもaono1234と申します。記事がいいなと思ったらtwitterのフォローもお待ちしております‼
https://twitter.com/takeshi_program
皆さん、コーディングは進んでいますでしょうか?
私はというと…まだプログラミング初心者のため、簡単なコードを書くのでさえ
数時間と掛かってしまいます
注意深くコーディングしても必ずエラーが発生してしまうんですよ!何とかならんものか…
エラーといっても原因は様々です、
- スペルミス
- メソッドの使い方が正しくない
- クラスの書き方がわかんない
etc
そんな超初心者な私でもデバッグ機能なしでエラー発生箇所を絞り込める方法があります。
良かったら、ご覧ください。
はじめに
本記事は以下の読者様が対象です。
-
プログラミング初心者
-
クラスや関数のなんとなくの流れが追える
-
何時間もコードとにらめっこしたけど解決できなかった
本記事ではRubyを題材に例題コードを作成しておりますが、考え方は他の言語でも適用可能です。
先に結論
デバッグ機能を使わなくても基本の考え方さえ押さえておけば
出力メソッドだけで、どこで問題が起きているか特定できる!
デバッグ機能が必要ないとは言ってないので勘違いしないでください。
初学者のために出力メソッドを使っているだけです。
ちなみに出力メソッドとは
- Ruby でいえば
puts
やp
- JavaScript でいえば
console.log
- C++ でいえば
printf
- PHP でいえば
echo
などのメソッドのことです。
これだけでピンとこない方は以下の例題で説明します。
例題
下記は「桃太郎」というパーティの合計のアタックポイントを出力するコードです。
class Momotaro
def initialize(self, first_member, second_member, thired_member)
@self = self
@first_member = first_member
@second_member = second_member
@thired_member = thired_member
end
def self_attack_point
if @self.empty?
0
else
100
end
end
def first_attack_point
if @first_member.empty?
0
else
100
end
end
def second_attack_point
if @second_member.empty?
0
else
100
end
end
def third_attack_point
if @thired_member.empty?
0
else
100
end
end
def total_attack_point
self_attack_point + first_attack_point + second_attack_point + third_attack_point
end
end
#パーティの設定
party_setting = { position_a: true, position_b: true, position_c: true, position_d: true }
#party_setting = { position_a: false, position_b: false, position_c: false, position_d: false }だと出力結果が0ではなく100となってしまう。
#変数へ格納(↓は三項演算子を使った書き方ですifのようなものです)
a = party_setting[:position_a] ? "桃太郎" : "" #party_setting[:position_a]がtrueならばaに"桃太郎"を入れて、falseならば""を入れる。
b = party_setting[:position_b] ? "犬" : ""
c = party_setting[:position_c] ? "猿" : ""
d = party_setting[:position_d] ? "鳥" : " "
#アタックポイントの合計値出力
momotaro = Momotaro.new(a, b, c, d)
p momotaro.total_attack_point
#出力結果400
#パーティ設定をすべてfalseにした場合:出力結果100
↑のパーティ設定の箇所ですが
position_a: true
のtrue
は「有効/無効ボタン」と思ってください。
true
であれば桃太郎がパーティに存在している
false
であれば桃太郎がパーティに存在していない
もしposition_a: false
であれば桃太郎抜きのパーティ(犬、猿、鳥)のアタックポイントを出すという具合です。
このコードにはバグがあります!
今party_setting[:position_○○]
が全てtrue
なので全員が有効となっております。
この時、アタックポイントは100 + 100 + 100 + 100
で出力結果は400となります。
逆にすべてのparty_setting[:position_○○]
をfalse
にすると
出力結果は0
になって欲しいです。
しかし出力結果は100というおかしな結果が出力されてしまいます。
これを解決していきます。
解決方法
問題が起きている場所を特定するのは
汚染(バグ、エラー)が発生した河川を下流から徐々に調べるようなイメージです。
まずは下流(出力に近いところ)から調べます。
(パーティ設定はすべてfalse
に変えてください)
#パーティの設定
party_setting = { position_a: false, position_b: false, position_c: false, position_d: false }
①
p momotaro.total_point #出力結果100
↑ここが一番下流である出力部です。
出力は100となってます。当たり前ですが、すでに汚染されてますね。
ではすこし上流のtotal_pointというメソッドの中を調べます。
②
def total_attack_point
puts self_attack_point #self_attack_point=0
puts first_attack_point #first_attack_point=0
puts second_attack_point #second_attack_point=0
puts third_attack_point #third_attack_point=100
return (self_attack_point + first_attack_point + second_attack_point + third_attack_point)
end
↑puts
メソッドで各変数の中身を見てみると
puts third_attack_point
だけ100と出力されています。
third_attack_point
がどうやら悪さをしているようです。
third_attack_point
を上流へ遡ります。
③
def third_attack_point
if @thired_member.empty?
puts "a" #出力されず!!!!
return 0
else
puts "b" #こっちが出力された!!!!
atack_point =100
return atack_point
end
end
↑ここでif
でちゃんと狙い通りreturn 0
のブロックを通っているかputsメソッド
をコード内に挿入して確認します。
正常ならば "a"
が出力されるのに"b"
が出力されています。
なぜelse
の方の処理が流れるのでしょうか?
@thired_member.empty?
がfalse
だからだと思いますが、念のため出力させて確認しましょう。
④
def third_attack_point
puts @thired_member.empty? #false
if @thired_member.empty?
return 0
↑やはりfalse
が出力されました。
@thired_member.empty?は@thired_memberの中が空であればtrue
を返します。そうでなければfalse
を返します。
原因は@thired_member
にありそうです。
@thired_member
の中に値が入っているのでしょうか?
遡り確認します。
⑤
def initialize(self, first_member, second_member, thired_member)
@self = self
@first_member = first_member
@second_member = second_member
@thired_member = thired_member
puts @thired_member #何も出力されず
end
↑
あれれ?@thired_member
には何も出力されません。何も値が入ってないなら@thired_member.empty?
はtrue
になるのですが…
なぜ@thired_member
は「空ではない(false
)」と判断されてしまうのでしょうか?
具体的な原因はまだ分かりませんが、この調査で範囲が絞れました。(調査済みの部分はコメントアウトしています。)
class Momotaro
def initialize(self, first_member, second_member, thired_member)
@self = self
@first_member = first_member
@second_member = second_member
@thired_member = thired_member
end
# def self_attack_point
# if @self.empty?
# 0
# else
# 100
# end
# end
# def first_attack_point
# if @first_member.empty?
# 0
# else
# 100
# end
# end
# def second_attack_point
# if @second_member.empty?
# 0
# else
# 100
# end
# end
# def third_attack_point
# if @thired_member.empty?
# 0
# else
# 100
# end
# end
# def total_attack_point
# self_attack_point + first_attack_point + second_attack_point + third_attack_point
# end
end
#パーティの設定
party_setting = { position_a: false, position_b: false, position_c: false, position_d: false }
#変数へ格納
a = party_setting[:position_a] ? "桃太郎" : ""
b = party_setting[:position_b] ? "犬" : ""
c = party_setting[:position_c] ? "猿" : ""
d = party_setting[:position_d] ? "鳥" : " "
#アタックポイントの合計値出力
momotaro = Momotaro.new(a,b,c,d)
p momotaro.total_point
少し手詰まりなので、次は上流(入力部)から調べてみます。
⑥
#パーティの設定
party_setting = { position_a: false, position_b: false, position_c: false, position_d: false }
p party_setting
#{:position_a=>false, :position_b=>false, :position_c=>false, :position_d=>false}
ここはすべてfalseが出力されており、問題ありません。
となると、問題部分は以下の⑦と⑤の範囲まで絞れたということになります。
⑦
#変数へ格納
a = party_setting[:position_a] ? "桃太郎" : ""
b = party_setting[:position_b] ? "犬" : ""
c = party_setting[:position_c] ? "猿" : ""
d = party_setting[:position_d] ? "鳥" : " "
↑見るとなにか違和感に気付くのでないでしょうか?
a
~c
の行は3項演算の右側が""
となっているのにdの行は" "
となっています。
原因はスペース(値)を間違って@thired_member
に入れてしまったことだと分かりました!
スペースを削除したら正常に動作しました!!
おわりに
如何でしょうか?
イメージは掴めましたか?
すべてのバグをこの方法で解決できるとは思いませんが、
「考える箇所」と「考えなくてよい箇所」を機械的に切り分けできるだけでも
分析が楽になるのではないでしょうか?
これからも色々と記事を投稿したいと思っておりますので、
どうぞ、よろしくお願いします!
追記
デバッグするときはputs をpに書き直した方が
より詳しい変数の情報を見ることができます!
test = gets #inputと入力してエンターキー押下
puts test #"input"が出力される
p test #"input/n"が出力される(制御文字も確認できる)