バベルの図書館は完成しない

Extended outer memory module
for my poor native memory.

Posts:
2020/09/28 gendoc という YAML からドキュメントを生成するコマンドを作った
2020/09/13 ISUCON10 の予選を 7 位で通過した
2019/12/01 Puma の内部構造やアーキテクチャを追う
2019/05/27 Golang の正規表現ライブラリの処理の流れをざっくり掴む
2019/04/29 InnoDB の B+Tree Index について
2019/04/29 InnoDB における index page のデータ構造
2019/04/28 InnoDB はどうやってファイルにデータを保持するのか
2019/01/06 Designing Data-Intensive Applications を読んでいる
2019/01/03 年末年始に読んだ本について、など
2019/01/01 Ruby から ffi を使って Rust を呼ぶ
2018/11/10 ブラウザにおける状態の持ち方
2018/07/01 Rust で web アプリ、 或いは Rust における並列処理
2018/05/14 なぜテストを書くのか
2018/05/13 Rust で wasm 使って lifegame 書いた時のメモ
2018/03/12 qemu で raspbian のエミュレート(環境構築メモ)
2018/03/12 qemu で xv6 のエミュレート(環境構築メモ)
2018/03/03 Ruby の eval をちゃんと知る
2018/02/11 Web のコンセプト
2018/02/03 Rspec のまとめ
2018/02/03 Ruby を関数型っぽく扱う

Ruby の eval をちゃんと知る

メタプログラミング(動的なメソッドや変数の生成)には欠かせない Ruby の eval やその周辺について。

きほん

eval "p 'hoge'"
# => "hoge"

eval "1 + 2"
# => 3

# eval で渡された引数を式として評価する

動的なメソッドやインスタンス変数生成

class Language
  def initialize(options = {})
    # インスタンス変数を生成
    options.each do |name, value|
      eval "@#{name} = #{value}"
      # attr_reader と同じ(getter を定義)
      eval "def #{name}; @#{name}; end"
    end
  end

  def my_define(options = {})
    # メソッドを生成
    options.each do |name, value|
      eval <<-EOS
        def print_#{name}
          p '#{value}'
        end
      EOS
    end
  end
end

lang = Language.new({haskell: 'functional'})

p lang.haskell
# => "functional"

lang.my_define({ruby: 'object-oriented'})
lang.print_ruby
# => "object-oriented"

instance_eval と class_eval

class Piyo; end

p1 = Piyo.new
p2 = Piyo.new

# ブロックを渡してそれを評価する

p1.instance_eval do
  def say
    p 'piyopiyo'
  end
end

p1.say
# => "piyopiyo"
p2.say
# => NoMethodError

Piyo.class_eval do
  def roar
    p 'ugaaaa'
  end
end

p1.roar
p2.roar
# どちらも => "ugaaaa"

instance_eval で private method 呼び出し

class SecretInfo
  def initialize
    @title = 'very secret'
  end

  private

  def details
    'this is secret, DO NOT USE THIS METHOD!!!'
  end
end

secret = SecretInfo.new

p secret.instance_eval { details }
# => "this is secret, DO NOT USE THIS METHOD!!!"
# private method にアクセスする方法のひとつ

無名関数との合わせ技

def books(prefix)
  lambda{|titles| titles.map {|title| "#{prefix} #{title}" } }
end

# 関数定義
my_book_maker = books "Effective"
# つまりこの時点で my_book_maker は
# lambda{|titles| titles.map {|title| "Effective #{title}" } }
# という Proc オブジェクト

p my_book_maker.call ["C++", "Ruby", "Haskell"]
# => ["Effective C++", "Effective Ruby", "Effective Haskell"]

Binding オブジェクト

def bind_method
  local_var = 'this is local'

  binding
end

eval "p local_var", bind_method
# => "this is local"
# eval の第二引数として Binding オブジェクトを渡すことで
# 第一引数の式の中で ローカル変数が取れるようになる

# ちなみに
p bind_method.class
# => Binding

Binding オブジェクトでインスタンス変数にアクセスする

class Language
  def initialize
    @name = "Haskell"
  end

  def bind
    binding
  end
end

fl = Language.new

eval "p @name", fl.bind
# => "Haskell"
# こんな感じで呼べるようになる

# binding はその文脈を外から再現するような感じっぽい

参考:

2018/03/03 15:00
tags: ruby
This site is maintained by furuhama yusuke.
from 2018.02 -