スタイルとデバッグ


読みやすいプログラミングスタイル

Ruby言語でプログラムを書く場合、文法的に合ってさえいればプログラムは動く。しかし、プログラムを作成していく過程では、何度も読み直して修正したり、他の人に見てもらう必要があるため、一目見ただけでプログラムの構造が分かりやすいような形式で書いていくことが望ましい。実際、構造が分かりやすいように気を付けて書いたものは完成までにかかる時間が短くなる。

プログラムを書く場合のスペースの空け方、括弧の置き方など、色々な流儀があるが、統一の取れたスタイルは読みやすい。以下のルールを守って書くとプログラムは非常に見やすくなる。


バグとデバッグ

コンピュータに与えるプログラムに含まれる論理的誤りのことをバグという。プログラムにひそんだバグを取り除く作業のことをデバッグという。デバッグするためには、プログラムが動くときのことを頭の中で追いかける必要があるので、文法エラーを取り除くのよりはるかに時間がかかる。プログラムを作る過程では、最初に書き進めて行く時間よりも後から間違いを直す(デバッグする)時間の方が長い。熟練するにつれ、デバッグにかかる時間が短くなり、同時に最初からバグの少ないプログラムが作れるようになる。

学習が進むにつれ作成するプログラムも徐々に複雑になっていくので、プログラミングに掛ける時間を節約する意味で、

を覚えておこう。

エラーを起こしにくい書き方

デバッグのしかた


デバッガ

上記のような方法でデバッグを行いたい場合はそのためにソースコードに printfp を挿入する必要があってあまり効率的ではない。ソースコードを修正せずにプログラムの動きを止めながら、1行ずつ実行して変数の値を確認することもできる。そのための道具をソースレベルデバッガと言う。コマンドラインでプログラムを起動する時に、通常の ruby [プログラムファイル] の代わりに、rdbg [プログラムファイル] と入力するとプログラムがデバッグモードで起動する。たとえば,第4回の授業の時に作った match_game.rb というプログラムをデバッグしたいのであれば

rdbg match_game.rb

と入力する。すると,デバッグモードに入って以下のような画面になる。

[1, 10] in match_game.rb
     1| #!/usr/koeki/bin/ruby
     2| # -*- coding: utf-8 -*-
     3|
=>   4| left = 17 # 残っているマッチ棒の数
     5|
     6| print "1から3本の範囲でマッチ棒を取ります。最後の1本を取った方の負け!\n"
     7|
     8| while left > 0
     9|   printf("あなたの番: マッチ棒が%d本あります\n", left)
    10|   print "何本取りますか?(1~3): "
=>#0    
at match_game.rb:4 (rdbg)

プログラムのソースコード(の一部)が表示され、次に実行される行のところに矢印(=>)がついている。

このモードでは (rdbg) というプロンプトが表示される度に、デバッガの操作を行うためのコマンドを入力できる。代表的なコマンドには以下のものがある。

その他のコマンドについては、こちらを参照すること(英文である)。

短いプログラムの場合は,s を打ち続けて1行ずつ実行を進めて,適宜変数の値を p で表示して確認する。 Return を空打ちすると直前と同じコマンドが繰り返し実行される。

長いプログラム,あるいはループで何度も繰り返すプログラムの場合は動きを調べたい場所の直前にブレークポイントを設定してから c で継続実行する。ブレークポイントを設定するにはソースプログラムの該当行番号を調べ、

(rdbg) b [行番号]

と入力する。

デバッグの例

以下は、ユーザーが入力した3つの整数で計算を行って結果を出力する簡単なプログラム debug_exercise2.rb であるが、ソースコードにバグがあって間違った結果になる場合がある。例えば、521 を入力すると 5 ÷ 2 + 1 という計算だから 3.5 という結果になるはずだが実際には 3.0 が表示される。

 練習問題  debug_exercise2.rb
#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

puts "整数を入力してください"
num1 = gets.to_i
puts "もう一つの整数を入力してください"
num2 = gets.to_i
quotient = num1 / num2
puts "もう一つの整数を入力してください"
num3 = gets.to_i
result = quotient + num3
printf("%d / %d + %d = %.2f\n", num1, num2, num3, result)

実行結果の例:

% ruby debug_exercise2.rb
整数を入力してください
5
もう一つの整数を入力してください
2
もう一つの整数を入力してください
1
5 / 2 + 1 = 3.0

デバッグを行ってみよう。

rdbg match_game.rb

と入力すると以下の内容が表示される。

rdbg debug_exercise2.rb
[1, 10] in debug_exercise2.rb
     1| #!/usr/koeki/bin/ruby
     2| # -*- coding: utf-8 -*-
     3|
=>   4| puts "整数を入力してください"
     5| num1 = gets.to_i
     6| puts "もう一つの整数を入力してください"
     7| num2 = gets.to_i
     8| quotient = num1 / num2
     9| puts "もう一つの整数を入力してください"
    10| num3 = gets.to_i
=>#0    
at debug_exercise2.rb:4 (rdbg)

s を入れてステップ実行すると最初の行(4行目)が実行されて「整数を入力してください」というメッセージが表示される。

(rdbg) s    # step command
整数を入力してください
[1, 10] in debug_exercise2.rb
     1| #!/usr/koeki/bin/ruby
     2| # -*- coding: utf-8 -*-
     3|
     4| puts "整数を入力してください"
=>   5| num1 = gets.to_i
     6| puts "もう一つの整数を入力してください"
     7| num2 = gets.to_i
     8| quotient = num1 / num2
     9| puts "もう一つの整数を入力してください"
    10| num3 = gets.to_i
=>#0    
at debug_exercise2.rb:5 (rdbg)

何も入力せずEnterキーを押すと直前に使われたコマンド(つまり s)が繰り返されて、次の行が実行される。そして1番目の整数(ここでは 5)を入力する。

(rdbg)
5
[1, 10] in debug_exercise2.rb
     1| #!/usr/koeki/bin/ruby
     2| # -*- coding: utf-8 -*-
     3|
     4| puts "整数を入力してください"
     5| num1 = gets.to_i
=>   6| puts "もう一つの整数を入力してください"
     7| num2 = gets.to_i
     8| quotient = num1 / num2
     9| puts "もう一つの整数を入力してください"
    10| num3 = gets.to_i
=>#0    
at debug_exercise2.rb:6 (rdbg)

pnum1 の値を表示すると期待通りに 5 が入っていることを確認できる。

(rdbg) p num1    # command
=> 5
(rdbg)

ステップ実行を繰り返してもう一つの整数を入力しそれも正しく保存されたことを確認する。

(rdbg) s    # step command
もう一つの整数を入力してください
[2, 11] in debug_exercise2.rb
     2| # -*- coding: utf-8 -*-
     3|
     4| puts "整数を入力してください"
     5| num1 = gets.to_i
     6| puts "もう一つの整数を入力してください"
=>   7| num2 = gets.to_i
     8| quotient = num1 / num2
     9| puts "もう一つの整数を入力してください"
    10| num3 = gets.to_i
    11| result = quotient + num3
=>#0    
at debug_exercise2.rb:7 (rdbg) 2 [3, 12] in debug_exercise2.rb 3| 4| puts "整数を入力してください" 5| num1 = gets.to_i 6| puts "もう一つの整数を入力してください" 7| num2 = gets.to_i => 8| quotient = num1 / num2 9| puts "もう一つの整数を入力してください" 10| num3 = gets.to_i 11| result = quotient + num3 12| printf("%d / %d + %d = %.2f\n", num1, num2, num3, result) =>#0
at debug_exercise2.rb:8 (rdbg) p num2 # command => 2 (rdbg)

もう一度 s を実行するとこれまで入力した2つの数字の割り算が行われる。

(rdbg) s    # step command
[4, 12] in debug_exercise2.rb
     4| puts "整数を入力してください"
     5| num1 = gets.to_i
     6| puts "もう一つの整数を入力してください"
     7| num2 = gets.to_i
     8| quotient = num1 / num2
=>   9| puts "もう一つの整数を入力してください"
    10| num3 = gets.to_i
    11| result = quotient + num3
    12| printf("%d / %d + %d = %.2f\n", num1, num2, num3, result)
=>#0    
at debug_exercise2.rb:9 (rdbg)

quotient に入っている割り算の結果を確認しよう。

(rdbg) p quotient    # command
=> 2
(rdbg)

本当は 2.5 のはずだが 2 になっている!これで、バグの原因は8行目で行われる計算にあるということが分かった。考えてみたら num1num1 はどちらも Integer(整数)だから割り算を行うと小数点以下の部分が切り捨てられてしまうのは当然である。以下のようにプログラムを修正すれば、num1num2 に保管される値は Integer ではなく Float(小数点数)として読み込みされて正しい結果になる。これでデバッグが完了した。

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

puts "整数を入力してください"
num1 = gets.to_f
puts "もう一つの整数を入力してください"
num2 = gets.to_f
quotient = num1 / num2
puts "もう一つの整数を入力してください"
num3 = gets.to_i
result = quotient + num3
printf("%d / %d + %d = %.2f\n", num1, num2, num3, result)


本日の課題(自由課題)

これまでの課題は、指示されたプログラムを作成するというものであったが、今回は自由にプログラムを作成する。これまでに学んできたことを活かして、何か面白いプログラムまたは役に立つプログラム(たとえば、簡単なゲームやクイズ、難しい数学関数を計算するプログラム、占いプログラムなど)を作成しよう。

提出要領:

本文は下記の通り記入してください.

氏名: 苗字名前
学籍番号: C11xxxxx

プログラムの概要:
...

使い方の説明:
...

ソースコード:
...

実行結果:
...

工夫した点:
...

応用したプログラミング技術:
...

参考文献:
...


目次