作ったもの、プログラミングで共有できそうなこと、IT関連でやってみたことなど、なんでも書いていきます。ヤドン可愛い

この前たのしいRubyを買いまして、絶賛勉強中です。
問題演習中にはまったところがあったのでまとめておこうと思います。

アルファベットとハイフンから構成される文字列を、ハイフンで区切って単語部分についてcapitalizeする

splitすることで簡単にできます。

def word_capitalize_split(str) # str = "ruby-php-Python-COBOL-JavaScript-javA"  
  nstr = []  
  result_split = ""  
  nstr = str.split(/-/) # ハイフンで分割し配列に格納  
  nstr.each do |word|  
    result_split << word.capitalize + "-" # 単語をCapitalizeしハイフンと一緒に文字列に入れる  
  end  
  return result_split.chop # "Ruby-Php-Python-Cobol-Javascript-Java"  
end  

しかし、この問題は正規表現クラスの章末問題でした。なので正規表現でも解いてみましょう。(実はもっと簡単にできたりしないかな・・・)

def word_capitalize_regexp(str) # str = "ruby-php-Python-COBOL-JavaScript-javA"  
  count = 0  
  str.each_char do |n| # 単語の中のハイフンの数を数える  
    if n == "-"  
      count += 1  
    end  
  end  

  if count > 0  
    separator = "(.+)"  
    for i in 1..count  
      separator << "-(.+)" #ハイフンの数だけセパレーターに追加する  
    end  

    Regexp.new(separator) =~ str # ハイフン区切りで単語をキャプチャ  
    result = $1.capitalize # 一つ目のキャプチャした単語をCapitalize  
        # ex.1 ハマったところここから  
    for i in 2..count + 1  
      result << "-" + eval('$'+i.to_s).capitalize # 二つ目以降のキャプチャした単語をCapitalize  
    end  
        # ex.1 ここまで  
    return result # "Ruby-Php-Python-Cobol-Javascript-Java"  
  else  
    return str.capitalize  
  end  
end  

ここで、メソッドに渡される文字列strにはハイフンがいくつ入ってくるかわからないので、キャプチャする単語数も動的に変化する点が問題となりました。
例えば、strにくる単語は"ruby-JavaScript-javA"かもしれないし、"ruby-php-Python-COBOL-JavaScript-javA"かもしれないという感じです。separatorは"(.+)-(.+)-(.+)"や"(.+)-(.+)-(.+)-(.+)-(.+)-(.+)"でええやろ、となったのですが・・・

evalによるコード評価

最初はex1部分は下のように書いてありました。

    for i in 2..count + 1    
      result << "-" + "$#{i}".capitalize    
    end    

しかし、この場合の結果は"ruby-php-Python" => "Ruby-$2-$3"となってしまいます。
どうやら、$2や$3が文字列オブジェクトではなくリテラルとして処理されてしまっているようです。
そこで、このリテラルがRubyのコードとして評価してもらえるよう、evalメソッドを使用します。

    for i in 2..count + 1  
      result << "-" + eval('$'+i.to_s).capitalize # 二つ目以降のキャプチャした単語をCapitalize  
    end  

これにより、文字列部分がプログラムとして実行され、想定どおりに結果が得られます。

参考

Rubyでメタプログラミングことはじめ

この記事へのコメント

12/15 13:13

split→capitlize→joinとわけなくとも素直にgsubでできるのではないでしょうか?

str = DATA.gets
puts str
#puts str.gsub(/([A-Za-z]+)/){ $1.capitalize }
puts str.gsub(/([^-]+)/){ $1.capitalize }
__END__
ruby-php-Python-COBOL-JavaScript-javA

実行すると

ruby-php-Python-COBOL-JavaScript-javA
Ruby-Php-Python-Cobol-Javascript-Java  

で、たぶん望んだ結果だと思うのですが。

12/16 00:00

@bogoさん

コメントいただきありがとうございます!

動作確認したところ、確かに望んだ結果を得られました。こういう方法もあるのですね・・・

ブロック変数の部分({ $1.capitalize })の挙動がいまいちわかっていないので頑張って理解します!