«前の日(11-28) 最新 次の日(11-30)» 追記

ema log


2007年11月29日 デバッグのやり方,ソースコードの読み方 この日を編集

_ [Programming] デバッグのやり方,ソースコードの読み方

mixi だと読みにくすぎるので、http://ema.fsr.jp/20071129.html で読むことを推奨します。

こういうのって、本にはなかなか書いてないし、あっても高い。良い本とかあるのかなぁ。知らないだけ?覚えてないだけ?

最近、そういう話題が多いので書いてみる。わかりにくいとか、変なこと書いてるとか突っ込み入れてもらえたら嬉しいです。

コツとしては、ソースコードと「にらめっこ」にないこと。

  • とにかく printf を入れてみる
  • とにかく書き換えてみる
  • とにかく実行してみる

とにかく、手を動かすことが大事。後、巻き戻せるように配慮しておく。 svn などを使うと revert できて安心(サーバにあるファイルに巻き戻せる)。

  • Google 先生に教えを請う
  • 友人に助けを求める

のも重要な手段。臨機応変に。とにかく悩んで、ソースコードとのにらめっこは非効率的。

プログラムってのは、右から左に受け流すもの

「入力 → 加工 → 出力」の組み合わせ。*1

ここで、「加工」の部分に目がいきがちなんだけど、「入力、出力」の部分から行くのが王道。 出発点とゴールが見えない山登りなんて無謀。

入力と出力を把握する

ドキュメントを読む(なかったり,間違ってたり)

ここで言う、入力出力とは、プログラム全体のものに限らず、関数や行単位のものも含む。ドキュメントがあるなら、それで概念をつかむ。でもドキュメントなんて無かったり間違っていたりは日常茶飯事。

んでもって、テストを書けばなお良いんだけど、それはひとまず置いておきます。

printf

とりあえず、いろんな場所に変数の中身を出力するコードを仕込みまくるのが定石。やなどとする。SEGMENTATION FAULT するのなら、デバッガを使っても良いしなどとどこまで動いているか力業で確認するのも一つの方法。自分用のデバッグ出力関数を持っておくと良い。

Visual Studio ならウォッチやデバッガから値を覗くのも一つの手。もちろん、gcc で gdb 使っても良いんですが、敷居が若干高い。

どんな値が出るか?、予想通りか?、違うのならどの時点でおかしくなっているのか?

色々入れてみて,出力をみる

入力の形式が分かれば、入力を変化させることが出来るようになる。色々いじってみて、どんな出力が出るのか把握する。

これは、中の挙動を把握する際にも有効な手段。まずは、どのような形式で出力されるのか?を把握することが大事。

中の挙動を把握する

やっぱり printf

解析範囲の規模が大きくなってきたら、内部状態や、途中経過を知るために printf を入れるのが無難。

A → B → C → D

の要になっているのが普通なので、B や C を printf すればいい。

適切な変数名や、B や C にする部分が関数で適切な名前がついていたら、類推が可能になる。名前重要。

○○はどこに? - よく分からない関数、変数が出てきたとき

Linux などなら、grep を使う。Windows でも grep と名前のついた何かを入れておくと便利。などとして使う。grepの簡単な使い方 が詳しい。

ソースを切り出して、動作を確認する

コンパイラ言語だと面倒だけど、スクリプト言語ならだいたいインタラクティブなシェルがあるのでそれを活用する。

Ruby なら irb。Ubuntu などだと、

sudo aptitude install irb

として、別途インストールが必要なので注意。

irb(main):001:0> [1,2,3]
=> [1, 2, 3]
irb(main):002:0> [1,2,3].join(',')
=> "1,2,3"

の用に使う。printf の際に inspect しておくと、irb にコピペできて便利。

irb 無しでコードを書く事なんて考えたくない。

JavaScript なら jashFirebug を使う。

C / C++ でも、テスト用の main を用意して、関数単位で動作確認するとデバッグやコード読みに便利。

デバッガ

デバッガというのは、プログラムの実行を途中で止めて(ブレイクポイント)中身を覗いたり、落ちた箇所を特定するツール。

使い方を覚える必要があるが、使えるようになると非常に便利。C / C++ 系では重宝する。

Ruby / JavaScript だと printf デバッグで事足りることが多い。

References
Ruby で debug する7つの方法
grepの簡単な使い方

*1 イディオム/慣用表現、は経験以外で獲得し得ないもの。他人のコードを読むしかない。


2008年11月29日 この日を編集

_ [最近]Twitter / やおっち: 3桁を三乗して下三桁が自身になる数の簡単な求め方を教 ...

寝る前に見かけてしまった、このTwitterが気になって仕方がない。頭をひねったのだけど、とんちはひらめかず。総当たり以外で解けるのかなぁ?へるぷみー。

> (0..999).find_all{|i| (i**3)%1000 == i }
=> [0, 1, 125, 249, 251, 375, 376, 499, 501, 624, 625, 749, 751, 875, 999]
> (0..999).find_all{|i| (i**2)%1000 == i }
=> [0, 1, 376, 625]

むー、二乗より、三乗の方が多いのはちょっと意外。でも、二乗でOKなら、三乗でOKなのは当たり前か。

(i*i*i)%1000 == (((i*i)%1000)*i)%1000

みたいなイメージ。ちなみに、

> (0..999).find_all{|i| (i**4)%1000 == i }
=> [0, 1, 376, 625]
> (0..999).find_all{|i| (i**5)%1000 == i }
=> [0, 1, 57, 125, 193, 249, 251, 307, 375, 376, 432, 443, 499, 501, 557, 568, 624, 625, 693, 749, 751, 807, 875, 943, 999]
> (0..999).find_all{|i| (i**6)%1000 == i }
=> [0, 1, 176, 201, 376, 401, 576, 601, 625, 776, 801, 976]
> (0..999).find_all{|i| (i**7)%1000 == i }
=> [0, 1, 125, 249, 251, 375, 376, 499, 501, 624, 625, 749, 751, 875, 999]

ふーむ??総当たりで、答えを出したら、より気になってきた。3乗と7乗が同じになるのは、i*i*i の後ろ二つの i を (i*i*i) に置き換えれば分かる??

i == (i*i*i)%1000 == (i*((i*i*i)%1000)*((i*i*i)%1000))%1000

だから?5乗と9乗などが等しくなるのは

i == (i*i*i*i*i)%1000 == (i*i*i*i*(i*i*i*i*i)%1000)%1000

みたいなのでOK?合同式はよく分かってないなぁ。13乗、25乗とも一致したしこんな感じで良いのかな?でも、

i == (i*i*i)%1000 == (i*i*((i*i*i)%1000)%1000)

とはならないしなぁ。