• ベストアンサー

Rubyの初心者ですが壁にぶちあたっております

『初めてのプログラミング』を読んでRubyを勉強しています。その中にある問題でp.127の『ローマ数字をアラビア数字に変換するプログラム』を書け、というのがありまして、最初に入力したローマ数字を弾く部分を書いているのですが…(以下コード) puts 'Enter roman numeral.' roman = gets.chomp.upcase roman_array = roman.to_a def char_checker(char) parts = ['I', 'V', 'X', 'L', 'C', 'D', 'M'] parts.each do |part| if part != char char = false elsif part == char char = true return char end end puts char_checker('X') (以上コード) このchar_checker, なぜかpartsを返してきます。返してほしいのはcharのtrueかfalseなのに泣 書いたコードに間違いがあるのはわかっているのですが、どこで間違っているのかがどうしてもわかりません。わかる方回答よろしくお願いします。ちなみに環境はMac OS X 10.5.4でrubyのバージョンはバンドルされている1.8.6です。

質問者が選んだベストアンサー

  • ベストアンサー
  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.4

>変数の使い回しとは配列のpartsと|part|のことでしょうか。 いえ、ちがいます。 def char_checker(char) ← のchar と char = false char = true で使っている char のことです。 > イテレータ部分の最初でcharがfalseに変えられてしまうの で、elsif part == charに引っかからなくなってました。 はい。そういうことです。 > 最後に評価された文だから'M'だけ出力されるはずなのに… いいところをついていますが、ちょっと違います。 確かにメソッドに return がない場合には最後に実行された文(式)の値が メソッドの返す値になるというのはそのとおりなんですが、 「最後に実行されたものは何か」ということです。 実はrubyでは、if 文なども値を返します。そして今回のように [配列].each do ... end も同じなんです。 irb というコマンドを使うと対話的にRubyが実行できるので試してみてもらいたいのですが、 irb(main):009:0> ['h', 'e', 'l', 'l', 'o'].each {|e| e[0]} => ["h", "e", "l", "l", "o"] irb(main):010:0> ['h', 'e', 'l', 'l', 'o'].each {|e| puts e[0]} 104 101 108 108 111 => ["h", "e", "l", "l", "o"] irb(main):011:0> この、=> の後に書かれているのがこの each を含んだ文の「値」です。 ということは、 def char_checker(char) このメソッドの「最後の文」は each do end の中にある文ではなくて、each している文そのものなんです。 ということで配列の中身であるI V X L C D M が全部出てきているというわけです。 そしてもうひとつ irb(main):001:0> def hoge irb(main):002:1> puts "hoge が呼ばれました" irb(main):003:1> "hello!" irb(main):004:1> end => nil irb(main):005:0> hoge hoge が呼ばれました ← メソッドの中の puts で出力されたもの => "hello!" irb(main):006:0> puts hoge hoge が呼ばれました ← メソッドの中の putsで出力されたもの hello! ← メソッド hogeの戻り値を puts した結果 => nil ← puts hoge の「値」 puts という「文の値」が nil なので > def文の最後にputs 'Lucky Stirike'と入れるとpartsの中身は出力されなくなりLucky Strikeと出力されました。 となるわけです。 こんなんで納得できますか?

sa1986
質問者

お礼

すっきりしました。irbすごい便利ですね!ありがとうございます。これからirbを駆使してこのプログラムを完成させたいと思います。またお世話になるかもしれませんが、その時はよろしくお願いします(^_^;)

その他の回答 (3)

  • asuncion
  • ベストアンサー率33% (2127/6289)
回答No.3

> どこで間違っているのかがどうしてもわかりません。 実行時のエラーメッセージの全文を正確に提示してください。 ところで、 > roman = gets.chomp.upcase > roman_array = roman.to_a この部分の意図は、どういったものでしょうか。 > puts char_checker('X') ここで'X'固定にしている訳も教えてほしいです。

sa1986
質問者

お礼

>実行時のエラーメッセージの全文を正確に提示してください。 えーと、エラーではなくてちゃんと実行されるのですが思った通りに動いてくれなかったんです。 >この部分の意図は、どういったものでしょうか。 これは実験で、Xだとどう動いてくれるかな~と思ってしました。 他の方の回答で解決しましたが、丁寧に回答していただきありがとうございます!!

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.2

変数の使い回しは止めましょう。 メモリを節約しているつもりかもしれませんが、意味ありません。 で、char_checker に一行書き加えて、何がどうなっているのかよく考えてみてください。 def char_checker(char) parts = ['I', 'V', 'X', 'L', 'C', 'D', 'M'] parts.each do |part| puts "char=#{char}, part=#{part}" #追加 if part != char char = false elsif part == char char = true return char end end end

sa1986
質問者

補足

アドバイスありがとうございます!!変数の使い回しとは配列のpartsと|part|のことでしょうか。メモリの節約とかそんな高度なことは考えてなかったんですが… 追加部分を加えて再度実行してみたところ、 bash-3.2$ ruby roman_arabian_again.rb char=X, part=I char=false, part=V char=false, part=X char=false, part=L char=false, part=C char=false, part=D char=false, part=M I V X L C D M と出ました。イテレータ部分の最初でcharがfalseに変えられてしまうので、elsif part == charに引っかからなくなってました。 puts "char=#{char}, part=#{part}"←こんなチェックの仕方があるとは知りませんでした。勉強になります! それで、配列"parts"の中身が出力されている件なんですが、試しにdef文の最後にputs 'Lucky Stirike'と入れるとpartsの中身は出力されなくなりLucky Strikeと出力されました。おそらくputs char_checker('X')で、char_checkerが"最後に"評価した文(この場合はif part != char)をputsに渡してるんだろうと思います。しかしそうだとすると最後に評価された文だから'M'だけ出力されるはずなのに… おこがましいですが何かお答えいただけたらと思います。

  • siffon9
  • ベストアンサー率64% (136/211)
回答No.1

このプログラムだとifに対応するendが無くてエラーになりますけど。

sa1986
質問者

お礼

ご指摘ありがとうございます。プログラム自身はちゃんと書いてあるんですが手打ちで書いたんでendが一つ抜けてしまいました。これからはコピペするようにします。

関連するQ&A

専門家に質問してみよう