打ち初め
第27回Smalltalk勉強会で話してきました
過去に「SmallTalk R4.1チャレンジ」というのをやったことがありますが、今年最後のネタふりとして、「super super, sub subチャレンジ」「マルチラインクラス・メソッドチャレンジ」というのを挙げておこうと思います。年末年始にかけて時間がある! という方はぜひいろいろな言語で挑戦してみると面白いのではないでしょうか。
Smalltalk勉強会でAAをクラス名やメソッド名にするというすばらしい発表があったので、Rubyでも(無理やり)やってみました。
マルチラインクラス定義。
# aa_classes.rb mclass " ____ / u \ / \ /\ 飽きやすい2ちゃねらのことだから / し (>) (<) \ そろそろ沈静化してるはず・・・ | ∪ (__人__) J | ________ \ u `⌒´ / | | | ノ \ | | | /´ | | | | l | | | " do mdef ' ____ / u \ / \ ─\ チラッ / し (>) (●) \ | ∪ (__人__) J | ________ \ u `⌒´ / | | | ノ \ | | | /´ | | | | l | | | ' do print "Hello, " end mdef ' ____ /::::::::::::::::\ /::::::─三三─\ /:::::::: ( ○)三(○)\ パ、パート53!! |::::::::::::::::::::(__人__):::: | ________ \::::::::: |r┬-| ,/ .| | | ノ:::::::::::: `ー"´ \ | | | /::::::::::::::::::::: | | | |::::::::::::::::: l | | | ' do puts "world!" end end
実際使ってみたところ。"..."でインスタンス化して、'...'でメソッド呼び出し。
#aa.rb require 'multiline' require 'multiline_parse' require 'aa_classes' " ____ / u \ / \ /\ 飽きやすい2ちゃねらのことだから / し (>) (<) \ そろそろ沈静化してるはず・・・ | ∪ (__人__) J | ________ \ u `⌒´ / | | | ノ \ | | | /´ | | | | l | | | " ' ____ / u \ / \ ─\ チラッ / し (>) (●) \ | ∪ (__人__) J | ________ \ u `⌒´ / | | | ノ \ | | | /´ | | | | l | | | ' ' ____ /::::::::::::::::\ /::::::─三三─\ /:::::::: ( ○)三(○)\ パ、パート53!! |::::::::::::::::::::(__人__):::: | ________ \::::::::: |r┬-| ,/ .| | | ノ:::::::::::: `ー"´ \ | | | /::::::::::::::::::::: | | | |::::::::::::::::: l | | | '
実行結果。1.8で失礼。
$ ruby -Ku aa.rb
Hello, world!
multiline.rbはこんな感じ。
#multiline.rb class Class def self.mclass(classname) (@@mclasses ||= {})[classname] end def self.mnew(classname, &block) (@@mclasses ||= {})[classname] = Class.new(&block) end def mdef(methodname, &block) name = "_aa_#{(@aa_methods ||= {}).size}" @aa_methods[methodname] = name self.class.__send__(:define_method, name, &block) end alias send_without_aa send def send(name, *args, &block) if @aa_methods.has_key?(name.to_s) send_without_aa(@aa_methods[name]) else send_without_aa(name, *args, &block) end end alias respond_to_without_aa? respond_to? def respond_to?(name, priv=false) if @aa_methods.has_key?(name.to_s) true else respond_to_without_aa?(name, priv) end end def send_and_self(methodname) send(methodname) self end alias + send_and_selfend module Kernel def mclass(name, &block) Class.mnew(name, &block) end end class String def mnew Class.mclass(self) end end
パーサー?の方はこう。
#multiline_parse.rb def parse_caller(at) if /^(.+?):(\d+)(?::in `(.*)')?/ =~ at file = $1 line = $2.to_i method = $3 [file, line, method] end end file, line, method = parse_caller(caller(1).first) if method == 'require' lines = File.open(file) {|f| f.readlines} code = lines[line..-1].join.chop. gsub(/^".*?"/m, '\&.mnew+'). gsub(/(^'.*?')\n/m, '\1+' + "\n"). sub(/"\+\Z/, '"'). sub(/'\+\Z/, "'") eval code exit end
Smalltalkみたいに言語からパーサーを弄るわけにいかないので、requireされたときにファイルの中身を読んで変形して解釈してそのまま終了。わりと卑怯な実装すね。
新年早々こんなですいません。今年もよろしくお願いします。