kiyokaのブログアーカイブ

Archive of old blog posts

Kyoto.Lisp Hackathon で作ったけどボツになったコード

Kyoto.lisp Hackathon #1 に参加してきた。 img 話としては(2012-08-11Nendo*Lisp Kyoto.Lisp Hackathon でやったことのまとめ) の続き。

本エントリの目的は、Hackathon当日の作業のうち直近ではボツだが、今後再利用できそうなテクニックをメモしておくこと。 次のような場面で使うスペシャルフォームをマクロ定義した。

ちょっと恣意的な例ではあるが、長いベクタに対して高階関数による多段処理をする例だ。 大きなテキストファイルをまるごと読み込んだArrayも長いベクタになるので、実用的なプログラミングでも良く出てくるパターンだ。 入力データは巨大だが、最終的に takeメソッドで最初の数要素のみ使い、あとは不要な場合は遅延評価を使って無駄な計算は省きたい。

(use srfi-1)

(let (*vec (Range.new 0 1000000)*)
  (for-each
   print
   (. 
    (map
     (lambda (x) (sprintf "<%05d>" x))
     (filter
      (lambda (num) (< num 100))
      vec))
    take 5)))

最後から2行目の vec を vec.lazy もしくは (lazy-vector vec)にすれば遅延評価を実現できるが、どこにlazyを指定するのが適当かを考えるのが面倒だ。 できれば、次のように(lazy S式)で囲めばその範囲で可能な限りよろしくやってくるのが嬉しい。(schemeのlazyと予約語がぶつかっているので、lazy-realmとかにしたほうが良いかな…)

(use srfi-1)

(lazy ;; この中のvector(すなわちRubyのArray型)は全てlazyに扱われる
 (let (*vec (Range.new 0 1000000)*)
   (for-each
    print
    (. 
     (map
      (lambda (x) (sprintf "<%05d>" x))
      (filter
       (lambda (num) (< num 100))
       vec))
     take 5))))

それを実現するのがHackathon当日に書いた次のマクロ。 健全なマクロを使っている。コードが長いので、gistに貼りつけた。 lazy special form sample — Gist

あとは%lazy-map、%lazy-for-each、%lazy-filterを関数で実装すれば、(lazy S式)で囲った中身のmap、for-each、filterの実装をlazy版に差しかえることができる。 便利そうな気もするが、影響範囲が大きいわりにはメリットが少ないような気もして、現段階では自分で(lazy-vector vec)する上の案に落ちついた。 それに、Nendoのsyntax-rulesはchibi-scheme 0.3からsyntax-rulesの実装のポーティングだが、ネストした “…” が扱えないという問題もあり、すっきり定義できていない。 このあたりも今後の課題として残る。

ただ、このテクニックを使ってpure(副作用無しのコードブロック宣言)も実現できるんじゃないかと考えているので、今後はそちらを試す予定。 pureのアイデアについてはこちらの過去記事を参照のこと。 2011-10-07Nendo*Ruby コードブロックに副作用が混在するかを検出できるかどうか こういう、できるかどうかわからないけど試す価値のある実験は1日かけて作業するHackathonが向いていると思った。