Vim/Objective-J Pluginに

投稿者 nanki 2010-11-14 20:19:00 GMT

初pull requestが来た!

一人以上ユーザが居ることがわかってよかった。

参考:

github - nanki/vim-objj


高度すぎるコードジェネレータ WLang

投稿者 nanki 2010-10-10 00:14:00 GMT

みなさんはコードジェネレータを使った事があるでしょうか。私はあります。

SQLにHTML, JavaScript, はてはPHPまで、Rubyでコードジェネレータを使う機会は増すばかりです。

さて、Rubyでコードジェネレーションと言えばERbを使うことが多いですが、それで充分なのでしょうか。 他の選択肢はどうでしょうか。

今日は、wlangというコードジェネレータを紹介します。

template engine?

次のサンプルコードを見てください。 とてもシンプルなHTMLテンプレートエンジンのように見えます。

require 'wlang'

puts WLang::instantiate(<<EOS, {:name => "O'Reilly & Animals", :animals => %w(Koala Rhino Giraffe)}, "wlang/xhtml")
<h1>${name}</h1>
<div id="{name}">
  <img alt='{name}' src="http://....."/>
  <ul>
    *{animals as animal}{${animal}}{, }
  </ul>
</div>
EOS

このプログラムの出力結果はこうなります。

<h1>O'Reilly &amp; Animals</h1>
<div id="O'Reilly & Animals">
  <img alt='O\'Reilly & Animals' src="http://....."/>
  Koala, Rhino, Giraffe
</div>

コンテキストに合わせて正しく、'&がエスケープされているのがわかります。 また、記法にはあまり馴染みがありませんが、配列の展開もうまく動いています。

wlang >> template engine

次にwlangの仕組みを見ていきます。 wlangでは、wlangタグを使って、テンプレートを記述します。 wlangタグは次のような形式です。

記号{.....}{.....}{.....}

後ろの{}はblockと呼ばれ、任意の個数持つ事ができます。 記号はたとえば $, *などです。

そう、実は先のテンプレートで出現した、${...}, "{...}, '{...}, *{...}{...}{...} は全て、wlangタグだったのでした。

(ちなみに、”{name}” の末尾の”はwlangタグとは関係なく、ただの文字列です。 これは <div id="{name}'>が文法エラーを出さずに動作する事で確認できます。このセンス嫌いじゃない)

なにやらあやしいかおりがしてきました。

wlang = programming language?

次に、WLang::instantiateの第三引数は”wlang/xhtml”となっていますが、WLangではこれをdialect(方言、なまりの意味)と呼んでおり、当然変えられます。

require 'wlang'

sql = "SELECT * FROM books WHERE name='{name}';"
values = {:name => "O'Reilly"}

puts WLang::instantiate(sql, values, "wlang/sql")
puts WLang::instantiate(sql, values, "wlang/sql/sybase")

# => SELECT * FROM books WHERE name = 'O\'Reilly';
# => SELECT * FROM books WHERE name = 'O''Reilly';

このように、dialect毎に'{...}タグの動作が変わるわけです。

dialectは複数のRuleSetとEncoderSetで定義されています。 RuleSetはタグの機能をオーバライドしたり新たなタグを定義します。 EncoderSetはdialect固有のencoding、つまり文字列の変換方法を定義していて、 ^{..encoding..}{...}というタグで呼び出す事ができます。

以下は大文字に変換するencodingの例です。

# instantiateとか省略します
^{plain-text/upcase}{wlang}
=> WLANG

これが例えばwlang/xhtml dialect であれば wlang/xhtml/main-encoding, wlang/xhtml/single-quoting, wlang/xhtml/double-quoting などが定義され、それぞれ違うエスケープがされるようになっています。

先のwlang/sqlwlang/sql/sybaseの結果の違いも、ここで決まっています。

勘のいい方ならお気づきだと思いますが、${...}, '{...}, "{...}はそれぞれdialectのmain-encoding, single-quoting, double-quotingを呼び出すためのショートカットになっています。

polyglot!

この様な拡張可能かつ再利用可能な変換規則を用意することで、Rubyの文字列を埋め込んだJavaScript、を埋め込んだxhtmlを埋め込んだRubyスクリプト、などといった複雑なコード生成を行う場合でも、 エスケープに悩まされることなく気楽にコード生成を行う事ができるwlangかっこいい!

でも、encodingの実装にバグがあるっぽくて、まだそれは無理っぽい。

参考:

HP PSC 1500というプリンタで名刺を刷ろうと思ったのだが、ミシン目のできないシール台紙のやつは厚みがありすぎて紙が滑り紙送りが正常にできない。

そんな時は、定規などで3mmくらい残して、両端のシールを剥がすとよい。 忘れずに、プリンタに最初に突っ込む方のシールも剥がしておく。

印刷範囲の拡張もできて便利!


死のイメージ

投稿者 nanki 2010-02-25 10:02:00 GMT

初めて人の死に接したのは、ひいおばあちゃんのお葬式で、4〜5歳くらいだった。
今でも覚えているが、別に悲しいことではなかった。
誰だかわかっていなかったし、親戚の家で行われた式だったが、その家にはお気に入りのおもちゃ群があって、僕が騒がないように親戚のお兄さんが遊んでくれた。

棺に花を詰め、霊柩車に載せて、火葬場まで行って、遺体が焼かれるのを見守った。
死んだ、というのはわかっていたが、生きている姿を見たことがなかったからか、眠っているような遺体をみんなで囲む会が催された、というだけだった。

それからしばらくして、バイオリンの練習をサボったかどで母親に怒られ、ふてくされて、布団にくるまりながら泣いている時に、初めて死を意識した。
といっても、母親に殺されそうになったとか、あまりのひどい仕打ちに自殺を考えたとかではなく、 母親の言い放った、空からひいおばあちゃんが見てるぞ、というような意味の言葉について考えていた。

全くくだらない脅しを言ったものだと思うけど、カーテンの隙間から曇った夜空を覗いてみて、今日は曇ってるから大丈夫だな、と思ったのを覚えている。

曇った空と、閉まったカーテンで、脅しの効果はなかったが、 その言葉から、死んだ後も意識が存続し続けるのだ、という概念を感じ取ったに違いない。

当時知り得た知識から、死とは、

  • 眠るようなもので、
  • 息をしてなくて、
  • しゃべれない、
  • 燃やされる、
  • 埋められる

というものだった。

意識の主体が、脳という物理的容器に束縛されていて(この認識の正しさは主張しないが)、 燃やされたらなくなってしまうということすらわかってなかったので、 眠りながら、息を止めて(ここまではやってみた)、燃やされて、埋められる、その間中ずっと喋れないのが何十年も(永遠に!)続くのを想像して怖くなり、 全然泣き止まなくて、この時ばかりは厳しくしすぎたと思ったのか、その晩だけは母親は優しかった、と思うのは美化された記憶かもしれない。

今では、死はそういうものではない、とわかってはいるものの、あの時見た死のイメージは今でも残っていて、死に対しては悲しさよりも恐怖、時には憤りすら覚える。


xargsの使い方

投稿者 nanki 2010-01-30 19:28:00 GMT

便利なxargs

UNIX系OSにはxargsというコマンドがあり、

$ cmd arg1
$ cmd arg2
$ cmd arg3
…

という処理を、次のように自動化してくれる。

$ cat args
arg1
arg2
arg3
…

$ cat args | xargs -n 1 cmd 

very venry!

疑惑

便利なxargsではあるが、オプショナルな引数をとるような処理を自動化しようとしたら、なかなか思う通りにいかなかった。
以下、Mac OSX 10.6.2 での話。

調査

$ cat args
arg1 opt
arg2
arg3
$ cat args | xargs ... ruby -e "p ARGV" ...

上のような入力をxargsに渡し、単純にコマンドライン引数をpするRubyプログラムを使って行う。

☷ cat args | xargs -n 2 ruby -e "p ARGV"
["arg1", "opt"]
["arg2", "arg3"]

xargs「神は空白と改行を平等におつくりになられた」

☷ cat args | xargs -I% ruby -e "p ARGV" %
["arg1 opt"]
["arg2"]
["arg3"]

惜しい。空白がエスケープされているので $ cmd "arg1 opt" と同じ。

☷ cat args | xargs -J% ruby -e "p ARGV" % 
["arg1", "opt", "arg2", "arg3"]

それは困ります…。

☷ cat args | xargs -E% ruby -e "p ARGV"
["arg1", "opt", "arg2", "arg3"]

ですよね〜。

☷ cat args | xargs -L 1 -J% ruby -e "p ARGV" %
["arg1", "opt"]
["arg2"]
["arg3"]

これだ!Lは世界を救う!

☷ cat args | xargs -L 1 ruby -e "p ARGV"
["arg1", "opt"]
["arg2"]
["arg3"]

xargs「よくぞたどりついた」


やはりRubyでは 〜Scalaの無名関数に憧れて〜

投稿者 nanki 2009-10-29 06:18:00 GMT

憧れシリーズ第二弾。

Scalaの勉強でもしようかな、と思ってScalaの本を開いたら、Scalaでは無名関数を_(アンダースコア)を使って定義できるらしい。 Rubyにおけるそれの実現可能性に考えが行ってしまって、Scalaの本を読むのはそれでおしまいになったのだ…

Rubyでも、

[1, 2, 3].map(_ * 2) # => [2, 4, 6]

とか書きたいよね。

Rubyにはもともと、mapなどのブロックを取るメソッドに無名関数(Proc)を引数として渡すことができる

[1, 2, 3].map(&:to_s)

この例では、:to_s は Symbol だが、Proc を返すSymbol#to_proc を定義しておくと、それが呼ばれてよしなにしてくれる。Symbol#to_proc はActiveSupport などで定義されているので、Rails使いにはおなじみだろう。

この仕組みを利用して、 _ に対して送られるメッセージを保存しておいて、to_proc される時に適当なProcを返すような何かを作ればよい。

module Scala
  class LazyCall
    instance_methods.each do |v|
      undef_method(v) unless %w(__id__ __send__).include?(v)
    end

    def initialize(v = nil)
      @v = v
      @msgs = []
    end

    def method_missing(*args, &block)
      @msgs << [args, block] unless args == [:respond_to?, :to_proc]
      self
    end

    def to_proc
      proc do |v|
        @msgs.inject(@v||v) do |r, m|
          r.__send__(*m[0].map{|a|LazyCall === a ? a.to_proc[v] : a}, &m[1])
        end
      end
    end
  end

  def _(v = nil)
    LazyCall.new v
  end

  # _0, _1, ...
  (0...3).each do |i|
    define_method("_#{i}"){LazyCall.new[i]}
  end
end

include Scala

そうしてできたのがこれ。

これを使うと…

%w(a b c d).map(&_ * _.to_i(16))
# => ["aaaaaaaaaa", "bbbbbbbbbbb", "cccccccccccc", "ddddddddddddd"]

[[1, 2], [2, 3]].map(&_0 + _1)
# => [3, 5]

_(Math).sqrt(_0 ** 2 + _1 ** 2).to_proc[[3, 4]]
# => 5.0

& の後に括弧が要ると思っていたがそんなことはなかったぜ。

メッセージをフックする関係上、直接 Math.sqrt(_0 ** 2 + _1 ** 2) とは書けないけど、そう書きたい人はブロック使ってください。

以下簡単な説明

  • _ はLazyCallのインスタンスを返し、受け取ったメッセージを引数と共に保存。
  • [:respond_to?, :to_proc] をはじいているのは、引数として渡される時にこの問い合わせが発生するので、それを無視するため。(副作用として、無名関数では.respond_to? :to_proc が使えなくなると思う)
  • to_proc内で、保存したメッセージを、procの引数に送り直している。
  • 保存された引数の中に、LazyCallのインスタンスがある場合は、call してその結果と置き換えている。これによって、_ * _ のような表記が可能になる。
  • _0は、_.[0]のエイリアス

二夜にわたるProxyObject特集、いかがでしたでしょうか。さて、来週からは…

追記:2009/10/30

Scalaの _ (プレースホルダ構文と呼ぶらしい) は _ + _と書いたとき、二つの _ が同じものを意味しないそうです。

と、閉じた本の続きに書いてあった。

あと、_0 などを使うと、20倍くらい遅い。どうしたものか。


今日のgolf - るびまゴルフ第七回

投稿者 nanki 2009-09-14 15:02:00 GMT

月に一度のgolf. というか、人がやっているのを目にすると、手を出したくなってくる。
たぶん、子供がプラモデル組み立てているのを見て「ちょっとお父さんにかしてみなさい」というタイプ。

結局、前回の最短は23Bだったらしい。変数名まで同じだった :)
文字数を削るという共通の目的が、変数名から意味すら奪うのだろうか。

20Bという噂はなんだったんだろう…

反省を生かして、今回は、27Bまで縮んだところで寝かせておく。

続きにあるのはお気に入りの答えだが、パー。

追記: 2009/09/17 いくつかの方針で使っている方法を組み合わせたら23Bになった。驚き。

参考:

今日のgolf - るびまゴルフ

投稿者 nanki 2009-07-02 23:14:00 GMT

久々にgolf。 問題はコチラ.

標準入力から、3,5 という入力を受け取って、3 から5 までの数字を出力するコード。

まず素直(?)に書いて、

p *eval(gets.split(',').join('..')) # 35B

順等に縮めると

p *eval(gets.sub',','..') # 25B

ここからは手強いので寝かせて。

eval'A,B='+gets;p *A..B # 23B

A, Bの重複がとても気になる…

ちょっとずるいけど、$ ruby -n を使っていいと、

p *eval(sub',','..') # 20B

文字列のRangeを使う方法もやってみたけど、挙動に問題があるのでやめ。

風の噂では、20Bまでいける、と聞いたんだけど…がんばります。

参考:

Rubyist Magazine - るびまゴルフ 【第 6 回】


Chemr高速化

投稿者 nanki 2009-04-21 18:46:00 GMT

今回のあらすじ

Flex のドキュメントを、オフライン時にも使えるようにしようと企んだ nanki は、coderepos:chm-generators を手にいれ chm ファイル作ってみるが、サイズが150MBもあるので、Chemr 本体に手を加えて、gzip 圧縮に対応することによって無事50MBにまでスリム化することに成功する。が、よく使うリファレンス群はそもそもサイズが5MB程度なので、たいして役に立たないことに気がつき衝撃を受ける。

これは巨大な chm を作って対抗するしかない、とマシンにインストールされた全ての gem の rdoc をchm 化するが、今度はインデックスが大きすぎるためか検索がもたつくことに気付く。

「これでは仕事ができない!」

不当な怒りに燃えた nanki は、よりよい開発環境を手に入れるために Chemr に改造を施し、高速化することを決意したのだった。

果たして戦いの果てに未来はあるのか!?

結果

一回のインクリメンタル検索処理あたり、0.3~1秒程度かかっていたのが100倍くらい速くなった。 体感では100倍とはわからないが、もたつきが感じられないレベルになった。

インデックスの最初の二文字を抜き出してHashで持ち、検索文字列の最初の二文字でO(1)でしぼりこむ仕組み。

高速化の恩恵は最初の数文字の偏りに大きく依存すると思うけど、そもそもそんな状況では、インクリメンタル検索が快適ではないので気にしないこと。

!DOCTYPE 宣言があると外部CSSが効かない(詳細不明)問題とか、ruby-cocoaでsize/countが無限再帰になる問題とか、Chmerとtypoする問題などにも嵌って大変だった。

その後、バージョン違いがたくさん(railsだけで5個くらい)あったgemを削除しまくったので、普通に速くなってあんまり意味がない。

gem を300種類くらい入れて複数のバージョンを保持しているような人しか恩恵が得られないかも。

ちなみに、もたつきが気になるというのは、僕のタイプ速度より速く、キーリピートより遅いくらい。つまり全然支障はない。

codereposのは古いっぽい…?

参考:

今日のActionScript - 三角形の外心

投稿者 nanki 2009-03-21 23:45:00 GMT

三つの点はドラッグ可能。

circumcenter - wonderfl build flash online

参考: