GHC のDebugger
GWも終わり、静けさを取り戻したと思っていたら ...
祝 FreeBSD 7.2 Release
リリースされましたね。GW中に! この時期のリリースは、日本人への配慮なの だろうか? 日本では、結構隠れ BSDerが居るので、連休中に思いっきり、Update でもして下さいって。GWを逃すと、十分な時間を取れないですから。
おいらは、先ほどcvsupしておいたので、buildworldは今夜決行かな。
祝 Clojure 1.0 Release
After a sustained period of API stability and minimal bug reports, I'm happy to announce the release of Clojure 1.0! http://clojure.googlecode.com/files/clojure_1.0.0.zip
今は、contribの改定がラッシュになっています。落ち着いたら antして、jarファイルを 置き換えておこう。
祝 Debug Hacks
ちょっと前の事だけど、Releaseされた。連休中には、その本の著者様達の居城で ある、ミラクル・リナックスさんを訪問していたのでした。残念ながら時間が無く て、実物を手に入れる事は出来なかったけど、機会が有ったら ...
前職では、ソフトを開発する部門に居たけど、debuggerを使っている人は誰も いなかったなぁ。Bugなんてないから使う場面が無いよと言うのなら、結構な のですが、printf派だったのですかね? 今となっては、知るよしもない。
私は、debugger大好き人間だす。これが無いと安心出来ません。使う場面が 無くとも、使い方を知っておくべき(保険としても)と、思っています。
最近、触り始めた、ghciでも prompt から、:? すれば、debugコマンドも サーと表示されます。が、画面を遡って見るのも面倒なので、見なかった 事にしてたんです。今日は、じっくりと見てみますかね。
ghci の debug command
-- Commands for debugging: :abandon at a breakpoint, abandon current computation :back go back in the history (after :trace) :break [<mod>] <l> [<col>] set a breakpoint at the specified location :break <name> set a breakpoint on the specified function :continue resume after a breakpoint :delete <number> delete the specified breakpoint :delete * delete all breakpoints :force <expr> print <expr>, forcing unevaluated parts :forward go forward in the history (after :back) :history [<n>] after :trace, show the execution history :list show the source code around current breakpoint :list identifier show the source code for <identifier> :list [<module>] <line> show the source code around line number <line> :print [<name> ...] prints a value without forcing its computation :sprint [<name> ...] simplifed version of :print :step single-step after stopping at a breakpoint :step <expr> single-step into <expr> :steplocal single-step within the current top-level binding :stepmodule single-step restricted to the current module :trace trace after stopping at a breakpoint :trace <expr> evaluate <expr> with tracing on (see :history)
debuggerと言えば、止めて、見るが基本なので、break print step continueぐら いは、基本中の基本だけど、abandon って、なんやねん?
学が無い私は、辞書が頼りです。最近知ったんですが、携帯にも、国語、英和、和英 が入っていました。オンラインで何処かのサイトから引いてくるのかと思ったら モバイル辞書とか銘打って、内蔵なんですね。便利だなあ!
さて、debugの材料であるが、何にしよう? 丁度、今読んでいる haskellerへの道 があるので、たらい回し を材料にします。
たらい回しの前に
emacs上から、C-c C-l すると、見事にエラーですよ。
Prelude> :load "tak.hs" [1 of 1] Compiling Main ( tak.hs, interpreted ) tak.hs:12:24: Not in scope: `intToDigit' tak.hs:13:55: Not in scope: `intToDigit' tak.hs:16:46: Not in scope: `digitToInt' Failed, modules loaded: none.
こりゃ、debugger以前の問題ですな。cut-seaさんは、hugsを使っていて、私は ghciを利用してます。多分、inportが足りないのでしょう。こういうのは、 執事に調べさせましょう。Hayoo!を 呼び出して、これお調べ。
Data.Char. intToDigit :: Int -> Char base Convert an Int in the range 0 15 to the corresponding single digit Char This function fails on other... Source
ありがとさん、import Data.Char しとけばいいのね。ついでに、この関数を使えば 16進数を文字に変換してくれる、なんて事まで分かって、お得です。
Prelude> :load "tak.hs" [1 of 1] Compiling Main ( tak.hs, interpreted ) Ok, modules loaded: Main. *Main> :main 200 100 0 Loading package bytestring-0.9.1.4 ... linking ... done. Loading package old-locale-1.0.0.1 ... linking ... done. Loading package old-time-1.0.0.2 ... linking ... done. Loading package filepath-1.1.0.2 ... linking ... done. Loading package Win32-2.2.0.0 ... linking ... done. Loading package directory-1.0.0.3 ... linking ... done. Loading package process-1.0.1.1 ... linking ... done. Loading package syb ... linking ... done. Loading package array-0.2.0.0 ... linking ... done. Loading package random-1.0.0.1 ... linking ... done. Loading package haskell98 ... linking ... done. 200 *Main>
今度は、成功しました。一応、成功した版を再掲します。
import System import Data.Char main :: IO () main = do args <- getArgs putStrLn $ numberToStr $ apply (tak) $ map (strToNumber) args apply :: (a -> a -> a -> b) -> [a] -> b apply f [x,y,z] = f x y z numberToStr :: Int -> String numberToStr num = if (num < 10) then [intToDigit num] else (numberToStr $ quot num 10) ++ [intToDigit $ rem num 10] strToNumber :: String -> Int strToNumber str = foldl1 (\x y->10*x+y) $ map digitToInt str tak :: Int -> Int -> Int -> Int tak x y z = if (x > y) then tak (tak (x-1) y z) (tak (y-1) z x) (tak (z-1) x y) else y
いよいよdebuggerだ
さて、どこで止めてみますかね。取りあえず、applyで止めますか。
*Main> :break apply Breakpoint 0 activated at tak.hs:9:0-24 *Main> :main 6 3 0 Stopped at tak.hs:9:0-24 _result :: b = _ [tak.hs:9:0-24] *Main> :list 8 apply :: (a -> a -> a -> b) -> [a] -> b 9 apply f [x,y,z] = f x y z ^^^^^^^^^^^^^^^^^^^^^^^^^ 10 [tak.hs:9:0-24] *Main> :step Stopped at tak.hs:6:49-70 _result :: [Int] = _ args :: [String] = [['6'],['3'],['0']] [tak.hs:6:49-70] *Main> :list 5 main = do args <- getArgs 6 putStrLn $ numberToStr $ apply (tak) $ map (strToNumber) args ^^^^^^^^^^^^^^^^^^^^^^ 7 [tak.hs:6:49-70] *Main> :step Stopped at tak.hs:9:18-24 _result :: b = _ f :: a -> a -> a -> b = _ x :: a = _ y :: a = _ z :: a = _ <emacsで、前コマンドの繰り返しALT-p を何度か実行> [tak.hs:20:38-40] *Main> :step Stopped at tak.hs:(20,28)-(22,46) _result :: Int = _ x :: Int = 5 y :: Int = 3 z :: Int = _
ちょっと例が深遠すぎましたが、計算の過程でどのように、引数が渡って行くかが 見てとれます。 詳細は、こちら が、参考になります。
ここまで書いていて何ですが、Haskellの場合、debuggerは最後の切り札に取って おくのがよさそうです。それより、型システムと言う強力な機構がありますので 関数単位にしっかりと機能チェックするのが、正しいHaskellerへの道だと感じました。