7
Help us understand the problem. What are the problem?

🔰超かんたん!初学者向けエラーのデバッグ

どうもaono1234と申します。記事がいいなと思ったらtwitterのフォローもお待ちしております‼
https://twitter.com/takeshi_program

皆さん、コーディングは進んでいますでしょうか?

私はというと…まだプログラミング初心者のため、簡単なコードを書くのでさえ
数時間と掛かってしまいます:arrow_heading_down:

注意深くコーディングしても必ずエラーが発生してしまうんですよ!何とかならんものか…:frowning2:

エラーといっても原因は様々です、

  • スペルミス
  • メソッドの使い方が正しくない
  • クラスの書き方がわかんない
    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に入れてしまったことだと分かりました!
スペースを削除したら正常に動作しました!!

おわりに

如何でしょうか?
イメージは掴めましたか?

すべてのバグをこの方法で解決できるとは思いませんが、
「考える箇所」と「考えなくてよい箇所」を機械的に切り分けできるだけでも
分析が楽になるのではないでしょうか?

これからも色々と記事を投稿したいと思っておりますので、
どうぞ、よろしくお願いします! :smiley:

追記

デバッグするときはputs をpに書き直した方が
より詳しい変数の情報を見ることができます!

test = gets #inputと入力してエンターキー押下
puts test #"input"が出力される
p test #"input/n"が出力される(制御文字も確認できる)

参考URL
https://qiita.com/nyaoo/items/f5d84581d3a301a9c22f

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
7
Help us understand the problem. What are the problem?