Diary?

2008-07-21
Mon

(01:37)

Haskell の学習をやり直しているうちに、何か print と putStr の動作の違いが気になった。次のコードは「ふつうの Haskell プログラミング」の例を、試しに print で実装してみたコード。

main = do cs <- getContents
          print $ map swapa cs
          
swapa 'a' = 'A'
swapa 'A' = 'a'
swapa c = c

putStr は即座に出力がされるのだけど、 print は EOF が来るまで何も出力されない。この差異はどこから来るのか?

GHC のソースを読むと、 print は実際には show と putStr の組み合わせで、次のコードと等価だ。

putStrLn $ show $ map swapa cs

その show の定義は GHC.Show で行われていて、 GHC.Show のコードは追っていると頭痛がしてくるが、定義を追っていくと Show 型では

  • show
  • shows
  • showsPrec
  • showList

などを定義しており、このうち showList の評価は引数の評価が終わるまで開始しないので、 getContents が全部終わってから評価が始まる。それでその処理内容だけど、どこでなにやってんのかさっぱりわからん。具体的には、処理の終わりが見えない。

何かむかついたので入力終了へのパターンマッチをしてるところを探したら、おそらく showList__ という showList の下請け関数でやってるという結論に達した。ただし、文字列の場合は showList を再定義して、そこで終了条件へのパターンマッチもしているようだ。

他の場合は showList__ から型に応じた showsPrec が呼ばれる。このとき、例えば Int 型なら showsPrec に showSignedInt が使われる。なんかいろんなところでダミー変数っぽい s が共通して使われているが、この s には初っ端のエントリポイントの show で空文字列が渡されていて、多分それだ。

とまあ一通りソースを追ってみたんだが、どうも show は普通に常識的な事をやっていて、むしろ妙なのは putStr に思えてきた。

実は俺はここまで手続き型脳で考えているので、 getContents で一行読んで次の処理が走ってというモデルを脳みそに構築している。でも考えてみれば、入力が全部終わってないのに出力のある putStr の方が本当なら注意が行かないとダメだよな。

というわけで、 IO と GHC.IO のソースを読んでみたのだが、件の putStr の実体といっていい hPutStr の定義がこれ。

hPutStr handle str = do
    buffer_mode <- wantWritableHandle "hPutStr" handle 
                        (\ handle_ -> do getSpareBuffer handle_)
    case buffer_mode of
       (NoBuffering, _) -> do
            hPutChars handle str        -- v. slow, but we don't care
       (LineBuffering, buf) -> do
            writeLines handle buf str
       (BlockBuffering _, buf) -> do
            writeBlocks handle buf str

やっぱ犯人はお前か! バッファリングの状態を調べて、それで逐次的に出力をしているわけか。ったく、これだから Haskell は……。いや、やっぱ手続き型脳の俺が悪いんだけど。

(20:09)

「となりの 801 ちゃん」三巻の限定版らしき小冊子が凄いことになってた。

いや、この場合は酷いといった方がいいのか?

(20:41)

コンピュータはなぜ動くのか 」を買ってきた。どうも今回の新人は基礎的な知識に絶望的に欠けてるようだから(さらに自力で調べもしねえし)、研修の足しになるかと思って。やっぱねえ、コンピュータの仕組みとかの基礎を押さえておいた方がその先を学びやすいと思うわけだよ。

それでパラパラと読んでみたんだけど、もしかするとこれはちょっと失敗だったかなあ。回路図の話あたりから始まるのはいいと思うんだけど、そっから OOP やら XML までってのは風呂敷を広げ過ぎに思える。まあ、一通り読んでから最終的な判断は下すことにする。


追記@21:10

第2章まで読み終わった。「.NET の説明それでいいの?」「DOS と Windows は説明して Unix とか Mac とかメインフレームは放置かよ」「ってかコンピュータの5大装置ぐらい教えろよ」とか突っ込みたくなるが、まあそこは適当にフォロー入れればいいか。それよかマイコン作成のところは実機がないとダメっぽいなあ。もうちっと座学っぽいっぽいものを期待してたんだけど、全然違った。

あと CPU の仕組みとか、ハードウェア内部の話が出てこないのはどうなんだろ。そこをブラックボックスにしちゃったら、この本の存在意義って?


追記@21:20

第3章はハンドアセンブルの話。さっきの章で出で来なかった CPU 内部のレジスタ構造がちょっとだけ出てきたが、詳しいものじゃなかったな。俺は不満だ。

「変換表とにらめっこしろ」で終わる話なので、全体的に薄味。


追記@21:40

第4章はフローチャートをメインにプログラムの処理の流れについての話なんだが……。

サンプルコードを VBScript で書くな!

何でいきなりウィンドウが出てくるようなコードを出すんじゃヴォケ。そもそもウィンドウが出てくるってことは、何かしらの形でイベントに触れないといけないはずなんだが、なぜかその後で WinAPI のちょろとした説明でお茶を濁してるし。それにさあ、今までアセンブリとかマシン語を見せていていきなりノー説明で VBScript はねぇだろ。

大体フローチャートってのはアセンブリ言語レベルの低水準言語の動作を説明するためのもので、それ以上の高水準言語になると、むしろフローチャートの方が記述水準が低くなってしまうんだが。

あと途中で出てきたダイクストラの構造化プログラミングについて一言。確かオリジナルのダイクストラの論文の趣旨は「恣意的なジャンプ命令(goto)を避けるための方法と言語の進化」みたいな感じだったはずで(うろ覚え。違うかも)、実際のところ近代的なプログラミング言語でもループからの break や関数からの return、そして例外処理といった「飼い慣らされた goto」を採用しているものが殆どで(手続きスタイルの言語は全部そうじゃない?)、屁理屈に聞こえるかもしれないけど goto はそこらに散らばってるんだよ。だから goto を使わなければいいというものじゃないの。

割り込み処理を通常のフローとの対比に持ってきてるのも変といえば変で、これはポーリングとの比較で使うべきだろう。せっかくハードウェアレベルの事まで書いてるんだから、ポーリングの説明も欲しかったところだ。

なんかやる気がなくなってきたけど頑張って読もう。


追記@22:26

アルゴリズムの章とデータ構造の章は、特に書くことなし。まあ、教養程度の内容ならこれでいいのかな。

しかし C のプログラムで変数も関数も ObjectName みたいな名前にしてんのは心の底からどうかと思った。これ刊行されたの 2003 年だろ? 俺が大学二年の時だ。そんときゃもうこんなネーミング規約は廃れていたような気がしなくもないが。


追記@22:41

あれ、 2003 年あたりって OOP の説明ってこんなんで良かったんだっけ。俺はもうちっとまともなものを習った覚えがあるぞ。

それで残りも一通り読んだけど、まあ結論としては素人に読ませるにしてもこれはないな。散漫な上に偏りすぎ。なんとなく予想はしてたけど、他人に物事を教えるときには咀嚼済みの本を使っちゃダメで、なるたけ堅い本を教える側が相手に合わせて咀嚼して説明しないとダメだ。

しかしそんな事やってる時間があるかっつーと、まあ無理なんだな。プログラミング研修だけでも大変だってのに、そこに計算機科学の基礎をきちんと教えてる時間が取れるかというと、まず取れない。いや、俺が研修だけやってりゃいいってんならまだどうにかなるけど、他の仕事もあるからなあ。

あと俺の持ってる本は上司曰く「堅すぎて他の社員も付いてこれない」そうなので、やっぱ結城先生の本に頼るしかないのか。ってか実際に「Java言語プログラミングレッスン」を研修で使ってるし。

Creative Commons
この怪文書はクリエイティブ・コモンズ・ライセンスの元でライセンスされています。引用した文章など Kuwata Chikara に著作権のないものについては、それらの著作権保持者に帰属します。