Posted at

遅延評価される文字列を作ってみた

More than 3 years have passed since last update.

新しくオープンした日本語版stack overflowを見ていたらこんな質問がありました。

http://ja.stackoverflow.com/questions/1606/rubyで文字列の式展開を後から行うには

読んでいた所、文字列内での変数展開(#{hogehoge})を遅延評価するようにできるかな?と、気になったので、ちょっと試しに作ってみました。

本来の質問とは離れた気がするけど、気にしない。


使用例

str = LazyString.new 'Curent Time: #{Time.now}'

puts str # => Curent Time: 2014-12-16 22:56:02 +0900
sleep 1
puts str # => Curent Time: 2014-12-16 22:56:03 +0900


クラス定義

class LazyString

def initialize str
@str = str
end

def to_s
eval %Q("#{@str}")
end

def to_str
to_s
end

def inspect
%Q("#{@str}")
end

def +(opponent)
to_str + opponent.to_str
end
end


問題点


Stringクラスを継承するとputs時にto_sが呼ばれなくなる

class LazyString < String

def initialize str
super
end

def to_s
puts 'LazyString#to_s'
'dummy'
end
end

puts LazyString.new('test')
# => test
puts LazyString.new('test').to_s
# => LazyString#to_s
# => dummy

予想外。


Stringクラスを継承していないので、色々な演算子を定義する必要あり

とりあえず+だけ定義してみた。

str = LazyString.new 'Curent Time: #{Time.now}'

str + ' is fixed'
# => "Curent Time: 2014-12-16 23:09:40 +0900 fixed"

評価しない+も欲しくなってくる。


スコープはあくまでLazyString

str = LazyString.new 'Hello, #{name}'

name = 'Sheile'
puts str
# => NameError: undefined local variable or method `name' for "Hello, #{name}":LazyString


まとめ

副作用が大きいので、実際に何かに使うことは無さそうですがStringを継承した場合の予期せぬ挙動とかも見れたので、なかなか面白い挑戦でした。