Listモナド

文字列を展開するメソッド2つ:

def expander( str, reg1)
  return [str] unless str =~ reg1
  r = [$1,$2,$3]
  yield(r[1]).inject([]) {|x,y| x << r[0]+y+r[2]}
  # 複数の展開対象があるかも.
  # 展開対象が無くなるまで再帰させる?
end
def expandCharClass(str)
  expander( str, /(.*)\[([^\]]*)\](.*)/) {|x| x.scan(/./)}
end
def expandAltWords(str)
  expander( str, /(.*)\{([^}]*)\}(.*)/)  {|x| x.split(/,/)}
end

p expandCharClass("img[012].jpg")
#=> ["img0.jpg", "img1.jpg", "img2.jpg"]
p expandAltWords("img.{png,jpg}")
#=> ["img.png", "img.jpg"]

をListモナドでくっつける.

class List
  def List::unit( val ); new([val]); end
  def List::empty; new([]); end

  def initialize( val ); @val = val; end
  def bind( &block )
    @val.map(&block).inject([]){|c,a| c+a }.to_list
  end
  def to_a; @val; end
  def empty?(); @val.empty?; end
end

class Array
  def to_list; List::new( self); end
end

p [1,2,3].to_list.to_a #=> [1,2,3]

def expandPattern( str)
  List::unit(str).bind {|x| expandCharClass(x) }.bind {|x| expandAltWords(x) }.to_a
end

p expandPattern("img[012].{png,jpg}")
#=> ["img0.png", "img0.jpg", "img1.png", "img1.jpg", "img2.png", "img2.jpg"]

もしかしてすごいのかも.
オリジナルはArrayを直接弄ってたけど,なんとなく新クラスにしてみた.

(追記 071227:このListは間違ってる気がする.emptyがList::new()でなく,ただのを返せばいい?モナド則的にはおかしいままだけど.)