実録 Debug

朝散歩していると、ちゃりんこに乗った小学生の一団に出合った。通学時間をとっくに過ぎているのに なんで、こうまとまってちゃりに乗っているんだろう?

いぶかしながら歩いていると、交差点のそこかしこに、おまわりさんやら、地区の交通指導員と おぼしき人が、黄色い旗を持って、交通整理をやっていた。田舎なんで、信号機の無い交差点が 多いから、事故でも起こっては困るという配慮だろう。

子供達は、体操着にヘルメットと言ういでたち。ヘルメットは、通学の時にかぶるのやら、保安帽 みたいのやら、サイクリングの時にかぶるのやら、はては、競輪選手みたいにかっこいいのやらと 色々。ちゃりも色々。ままちゃりを小さくしたのやら、サイクリング用やら、レーサータイプの ものまで。

次の日、別の小学校の前を通ったら、グランドが銀輪と黄色い歓声で溢れてた。そうか、春の一斉 交通安全週間なんだな。それぞれの学校で日程をずらして、交通安全の指導をしてるんだ。 暫く見てたら、一斉にちゃりの一団がグランドを出発していきましたよ。一瞬、ここは中国ですか と、思っちゃったぞ。

昔々、北京とやらへ行った事があるけど、朝の通勤・通学時間帯はちゃりが溢れていましたねぇ。 自動車よりもチャリ優先。交差点の真ん中でおまわりさんが、交通整理をしてたなあ。で、おいらも 早速泊まっていたホテルでチャリを借りて、中国人に成り済ましたのでした。どこまで走っても、 真平らで気持ちよく走れたよ。懐かしいね。

そろそろ、ちゃりを転がすにはいい季節だ。久しぶりに走ってみようかな。この地、結構登りが きつくて、ヘトヘトになるかもです。

(林檎の樹観察日記) 若々しい色の芽が出てきた。次は花だな。何時咲くのだろう。楽しみ!

Errorを知れ

Pythonを使ってグラフを書いてみるシリーズもこれで3回目。いいかげん飽きてきたので、今回で打ち止めに しょうと思う。そんな訳で、きっかけになったアンテナアナライザーのソフト説明書を改めてみたら、 リターンロスの表示なんてのが目に入ってきた。どんなものか、やってみる。

前々回だったか、csvモジュールでCLのリーダーマクロみたいな事が出来ればいいね、なんて事を書いた。 そんな事が出来るか調べてみたんだけど、どうもそこまで尖がった機能は無いようだ。あるのは、EXCEL方言 で読み込むとか、パスワードファイルをcsvファイルと看做して読むとか、そんな文字列操作だけだ。

だったら、ちょいと手を加えて、getlogの中に押し込めちゃおう。押し込める機能は、csvもどきを読み込んで 結果を、周波数、インピーダンスの形にするやつ。書いてみた。

def getlog(csv_file):
    with open(csv_file) as fd:
        s = fd.readlines()
    frx = [x for x in s if re.match(r"^\d.*", x)]
    return [(float(x[0]), complex(float(x[1]), float(x[2]))) for x in frx]

そして、pythonのプロンプト上で走らせたら、見事にエラーだよ。

Traceback (most recent call last):
  File "C:/homes/WORK/loss.py", line 39, in <module>
    if __name__ == '__main__': main()
  File "C:/homes/WORK/loss.py", line 33, in main
    fz = getlog('AA30.log')
  File "C:/homes/WORK/loss.py", line 14, in getlog
    return [(float(x[0]), complex(float(x[1]), float(x[2]))) for x in frx]
ValueError: could not convert string to float: .
>>> import pdb
>>> pdb.pm()
> c:\homes\work\loss.py(14)getlog()
-> return [(float(x[0]), complex(float(x[1]), float(x[2]))) for x in frx]
(Pdb) p x
'6.900000,60.95,-38.11\n'
(Pdb) q

こういう時は、慌てず騒がず、pdbと言うPython用のdebuggerを呼び出して(importして)、エラーの 原因を解析すれば良い。効を焦りすぎて、csvでパースするのを忘れておったわい。

def getlog(csv_file):
    with open(csv_file) as fd:
        s = fd.readlines()
    frx = csv.reader([x for x in s if re.match(r"^\d.*", x)])
    return [(float(x[0]), complex(float(x[1]), float(x[2]))) for x in frx]

上記のように修正したよ。

全体のDebug

修正して走らせてみたら、グラフが表示されなかったよ。虫が一匹見つかると他に居るぞって戒めは 当たっているね。(感心してる場合か!) 但し、Pythonからは何のお咎めも無し。こういう時は、pdbを普通に使ってみる。そう、rubyでdebuggerを 使うのと一緒の要領だ。

C:\homes\WORK>python -m pdb loss.py
> c:\homes\work\loss.py(1)<module>()
-> import re
(Pdb) h

Documented commands (type help <topic>):
========================================
EOF    bt         cont      enable  jump  pp       run      unt
a      c          continue  exit    l     q        s        until
alias  cl         d         h       list  quit     step     up
args   clear      debug     help    n     r        tbreak   w
b      commands   disable   ignore  next  restart  u        whatis
break  condition  down      j       p     return   unalias  where

Miscellaneous help topics:
==========================
exec  pdb

Undocumented commands:
======================
retval  rv

(Pdb) h pp
pp expression
Pretty-print the value of the expression.

起動したら、pdbのプロンプトが出てきた。ppなんてコマンドが有るんか。

(Pdb) b main
Breakpoint 1 at c:\homes\work\loss.py:26
(Pdb) c
> c:\homes\work\loss.py(28)main()
-> fz = getlog('AA30.log')
(Pdb) l
 23         g('set data style lines')
 24         g.plot(dat)
 25
 26 B   def main():
 27         #subprocess.call(['ttpmacro', 'sa.ttl'])  # sa.ttl must be in teraterm-dir
 28  ->     fz = getlog('AA30.log')
 29         dat = map(loss, fz)
 30         graph(dat)
 31         raw_input('Please press return to end...\n')
 32
 33     # when executed, just run main():

普通に、mainへbpを置いて継続実行してみた。なお、-> は、止まってる場所ね。B は、bpが設定されてる 行か。CUIだと苦労してるね。

(Pdb) n
> c:\homes\work\loss.py(29)main()
-> dat = map(loss, fz)
(Pdb) n
> c:\homes\work\loss.py(30)main()
-> graph(dat)

ちょっと先へ進めてから、gnuplotの表示ルーチンへ渡すデータを確認

(Pdb) pp dat
[(6.9, 9.4216182063861584),
 (6.904, 9.490277388103129),
 (6.908, 9.6853712081378944),
    :
 (7.292, 6.0375391405844052),
 (7.296, 5.9975322990725797),
 (7.3, 5.946130964684496)]

特にデータには異常なさそうなので、表示ルーチンへ下りて行きます。

(Pdb) s
--Call--
> c:\homes\work\loss.py(20)graph()
-> def graph(dat):
(Pdb) s
> c:\homes\work\loss.py(21)graph()
-> g = Gnuplot.Gnuplot(debug=1)
(Pdb) r
gnuplot> set terminal windows
gnuplot> set title "AA-30 Return loss"
gnuplot> set data style lines
gnuplot> plot "c:\\users\\foo\\appdata\\local\\temp\\tmp35pan5.gnuplot" notitle
--Return--
> c:\homes\work\loss.py(24)graph()->None

s(tep)コマンドを使ったら、モジュールの中まで入って行ってしまいましたので、rを使って、呼び出しの 最後まで、ザーと実行しました。そしたら、不思議な事にグラフが表示されましたよ。これが噂のdebuggerを 動かすとBugが引っ込んじゃうと言う奴ですか。

(Pdb) where
  c:\python27\lib\bdb.py(383)run()
-> exec cmd in globals, locals
  <string>(1)<module>()
  c:\homes\work\loss.py(34)<module>()
-> if __name__ == '__main__': main()
  c:\homes\work\loss.py(30)main()
-> graph(dat)
> c:\homes\work\loss.py(24)graph()->None
-> g.plot(dat)

これ、呼び出し履歴ね。

更に、n(ext)で実行を続けると、raw_inputを呼び出した所で、グラフが消えましたよ。こりゃ、raw_input の呼び出しを、graphの中へ持って行って、表示用のテンポラリーファイルが残るようにしないと、だめだな。 それに、plotを抜けた時に、表示のインスタンスが消されちゃう大問題が有るからね。

で、最終的に出来上がったのがこれ

import re, csv, subprocess
from numpy import *
import Gnuplot

def getlog(csv_file):
    with open(csv_file) as fd:
        s = fd.readlines()
    frx = csv.reader([x for x in s if re.match(r"^\d.*", x)])
    return [(float(x[0]), complex(float(x[1]), float(x[2]))) for x in frx]

def loss(fz):
    f,z = fz
    return f, -20 * log10(abs((z -50) / (z + 50)))

def graph(dat):
    g = Gnuplot.Gnuplot()
    g.title('AA-30 Return loss')
    g('set data style lines')
    g.plot(dat)
    raw_input('Please press return to end...\n')

def main():
    subprocess.call(['ttpmacro', 'sa.ttl'])  # sa.ttl must be in teraterm-dir
    fz = getlog('AA30.log')
    dat = map(loss, fz)
    graph(dat)


# when executed, just run main():
if __name__ == '__main__': main()

要のmain()が綺麗になったな。データを読んで、計算して、表示する。read-eval-print の出来上がりですよ。 いつも、こう綺麗になればいいんだけどね。って言うか、これって何処にも隠れている、パターンだよ。 Gofをやる前に、これは押さえておきたいね。

ああ、それから関数型プログラミング HOWTOPython 良い慣用句、悪い慣用句には、眼を 通しておいた方が良いな。