Gauche > Archives > 2012/06/28

2012/06/28 03:05:23 UTCとおる。
#
C のプリプロセッサやコンパイラがバイナリにデバッグ情報をくっつけるような感じですよねぇ。それってやっぱりオーバーヘッドとはトレードオフの関係になるんじゃないでしょうかね。あるいは加工の過程を全部見張っているビッグブラザーみたいな存在がいて、必要なときはそれにお伺いを立てるとか? あ、それがモナド的なアプローチになるんですかね。
2012/06/28 03:15:15 UTCshiro
#
「コンパイラ」等、ひとつのプログラムを組むにあたってはその目的や使い方によってトレードオフのバランスを決められるんですが、言語ライブラリとして「加工の一過程に使える部品」を提供する時は、どこでバランスを取るべきかはっきり見えませんよね。理想的には「一つのライブラリで、使う側が使い方によってバランスを決められる」ってのがいいんですが。モナドは、裏で余分な情報を受け渡すコードをモナド側に隠してしまえるので、「裏の情報が受け渡される場合/裏に情報なんか無い場合」でコードを共有できます。余分な情報を使うコードと組み合わせれば全体として余分な情報を受け渡せるし、そういうコードと組み合わせなければオーバヘッドはない(原理的には)。でも動的型なSchemeではモナドみたいな仕組み自体にかなりオーバヘッドが生じちゃうんですよねー。
2012/06/28 04:24:02 UTCyamasushi
#
ジェネレータに限って言えば、「多値のジェネレータ」をつかえばいいのかなとか思いました。(どういう影響があるのか見えないのですが。)
2012/06/28 04:28:28 UTCshiro
#
はい。メインのデータに付随して余分なデータを別の値で返す、という使い方のために多値ジェネレータは有望だと考えています。ただ、動的型は「関数を呼ぶ前に戻り値についての情報を得ることができない (ここでは、ジェネレータ手続きを呼ぶ前にそれが多値を返すか知ることができない」ため、なかなか綺麗に組み合わせる方法が思いつきません。ジェネレータを(単なる手続きでなく)特殊なオブジェクトにして、メタ情報を問い合わせられるようにすることも検討しましたが、Scheme本来の手続きによる抽象化が素直に使える現在の形にひとまず落ち着きました。
#
かなり迷ったんですよ。port->char-generatorで二番目の値に先頭からのオフセットや行番号を返すやつとか入れようかなどうしようかなと。でも静的型だと上流で切り替えれば型推論で下流も全部切り替わってくれるけど、Schemeだと上流入れ替えたらそれに対応して下流の方も直していかないとならなくて。
2012/06/28 04:32:46 UTCyamasushi
#
一般には多値かどうかは決められないが、I/O由来のジェネレータなら多値を期待して、そのように扱うという方向なのかなと。なにも期待しないなら今までどおりで多値の部分は捨てるという。
2012/06/28 04:35:09 UTCshiro
#
多値を捨てるのは正式仕様じゃないですよ。採用すると、透過でない (本来等価であるはずの式変形をすると情報が失われる) 場合が出るので採用したくはないです。
2012/06/28 04:36:24 UTCyamasushi
#
ああ、そうだったんですか。なるほど、以前のお話の意味がなんとなくわかってきました。
#
手続きで不定個の値を返すのはご法度なわけですね。
2012/06/28 04:39:56 UTCshiro
#
ご法度じゃないですけど、受ける方がそれを知ってて常にリストで受けないとならないので、あまり意味が無いというか (リストで受けるしかないなら最初からリストで返せばいい)
2012/06/28 04:46:51 UTCyamasushi
#
「多値を捨てる」のが正式なら不定個でも先頭だけとって扱う場合とリストで受ける場合の二通りの処理方法があるなあとおもっていたのです。それが正式とはいえないなら、不定個の値を返す手続きは確かに意味がないです。
2012/06/28 08:05:03 UTCshiro
#
「配列をソートしてから要素を加算したら、ランダムな配列よりうんと速くなった。なんで?」に対する回答がすばらしく親切でわかりやすい。 http://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-an-unsorted-array
2012/06/28 12:52:43 UTCyamasushi
#
(define (open-with-input-files files proc)
  (if (null? files)
    (proc (current-input-port) )
    (for-each 
      (lambda (f)
        (call-with-input-file f proc))
      files)))

;; grepのメイン
(define (main args)
  (if (null? (cdr args))
      (usage)
      (let ((rx (string->regexp (cadr args))))
        (open-with-input-files (cddr args) (cut grep rx <>))))
  0)

;; catのメイン
(define (main args)   ;entry point
  (open-with-input-files 
    (cdr args)
    (cute copy-port <> (current-output-port))))
#
マニュアルのcat,grepの共通部分をopen-with-input-filesとしてみました。これを抽象化(?)すればいいのかなあとか。
2012/06/28 12:56:48 UTCshiro
#
なるほどー。ポートを個別に渡すんですね。これはこれで便利っぽいけど、もう一捻りいけそうな気も。どこがどうというのは今わからないけど。
2012/06/28 13:00:31 UTCyamasushi
#
ポートの連結ができると、それはそれで素晴らしいと思います。
2012/06/28 13:04:20 UTCshiro
#
どうも気になるのが「引数が()ならcurrent-input-portから」ってとこで、これはunixコマンドと整合性があるんだけどSchemeの他の場所とは整合性がない (他の場所は、()なら何もしない、を期待するでしょうから)。なので関数名を工夫して「コマンド書く時を想定してるよ」って香りを強烈に出すか、それとも「引数が()ならcurrent-input-port」を簡潔に明示できる何かがあるといいかなあ、って気がしてる。
2012/06/28 13:08:43 UTCyamasushi
#
「コマンドライン専用」にすれば、さらに(usage)の処理も入れることができますね。
#
うまく、表現できていないのですがusageを表示する方式には型があるように思うので。
2012/06/28 13:14:52 UTCshiro
#
そうですね。usageはgauche.parseoptでうまく処理したいんですが、「簡単なケースは簡潔に、難しいケースも処理できる」綺麗な落としどころをまだ見つけられずにいます。