打ち初め

第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されたときにファイルの中身を読んで変形して解釈してそのまま終了。わりと卑怯な実装すね。

新年早々こんなですいません。今年もよろしくお願いします。