Ruby初心者です。
まだRubyをはじめて本日で3日目の者です。
今までPHPで開発してきたのですが、Rubyを勉強することにしてみました。
FizzBuzzなどその他の問題をやりながら勉強しています。
今回は、Rubyで閏年問題プログラムを作ってみようと思い書いてみました。
以下コードです。
lang
1def days(year, month) 2 if (year % 4 == 0 && year % 400 == 0 || (year % 100 != 0)) 3 if (month == 1 || 3 || 5 || 7 || 8 || 10 || 12) 4 return 31 5 elsif (month == 4 || 6 || 9 || 11) 6 return 30 7 elsif (month == 2) 8 return 28 9 end 10 else 11 if (month == 1 || 3 || 5 || 7 || 8 || 10 || 12) 12 return 31 13 elsif (month == 4 || 6 || 9 || 11) 14 return 30 15 elsif (month == 2) 16 return 29 17 end 18 end 19end 20puts "年を入力してください" 21year = gets.to_i 22puts "月を入力してください" 23month = gets.to_i 24days = days(year, month) 25puts "#{year}年の#{month}月は#{days}日です。"
かなり冗長なコードで良くないアルゴリズムですが、これは正しいプログラムなのでしょうか?
間違っていたら添削としてアドバイスを頂たいと思っています。
また、アンサーの方が今回の閏年問題プログラムを作成した場合、どのように
コードを書いたか別解も載せて頂ければかと思います。
宜しくお願いします。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答3件
0
ruby の学習という点では、自分でいろいろな条件判定を行うコードを書くのがよいのですが、
アプリケーションの中でつかうなら、既存ライブラリーをつかうほうが、バグがすくなくなります。
私なら次のようにしてしまいます。
lang
1# coding: utf-8 2require 'date' 3 4def days(year, month) 5 (Date.new(year, month, -1) - Date.new(year, month, 1)).to_i + 1 6end 7 8(2000..2001).each do |y| 9 (1..12).each do |m| 10 puts "#{y}年の #{m}月は #{days(y, m)} 日です。" 11 end 12end
実行結果は次のようになります。
2000年の 1月は 31 日です。 2000年の 2月は 29 日です。 2000年の 3月は 31 日です。 2000年の 4月は 30 日です。 2000年の 5月は 31 日です。 2000年の 6月は 30 日です。 2000年の 7月は 31 日です。 2000年の 8月は 31 日です。 2000年の 9月は 30 日です。 2000年の 10月は 31 日です。 2000年の 11月は 30 日です。 2000年の 12月は 31 日です。 2001年の 1月は 31 日です。 2001年の 2月は 28 日です。 2001年の 3月は 31 日です。 2001年の 4月は 30 日です。 2001年の 5月は 31 日です。 2001年の 6月は 30 日です。 2001年の 7月は 31 日です。 2001年の 8月は 31 日です。 2001年の 9月は 30 日です。 2001年の 10月は 31 日です。 2001年の 11月は 30 日です。 2001年の 12月は 31 日です
参考
ActiveSupport というライブラリーを使うなら、days_in_month というそのものズバリのメソッドがあります。
参考:
- days_in_month http://apidock.com/rails/ActiveSupport/CoreExtensions/Time/Calculations/ClassMethods/days_in_month
ページ下部にある show source をクリックするとソースコードをみることもできます。
投稿2014/11/24 22:05
総合スコア22324
0
ベストアンサー
年月と日数の関係は正しいものとして、話を進めていきます
書き換えたあとのコードをぱっと出すよりは過程も載せたほうが良いとおもって、かなり長くなっています
しかし、参考になれば幸いです
- まず初めにRuby っぽい書き方に換えていきます。うるう年判定もライブラリのメソッドを利用します
※ 関数のなかでの return
は明示的に値を返さなければならない場合を除いて 省略することが出来ます
lang
1require 'date' 2def days(year, month) 3 # if (year % 4 == 0 && year % 400 == 0 || (year % 100 != 0)) 4 if Date.leap?(year) 5 # if (month == 1 || 3 || 5 || 7 || 8 || 10 || 12) 6 [1, 3, 5, 7, 8, 10, 12].include?(month) 7 31 8 # elsif (month == 4 || 6 || 9 || 11) 9 elsif [4, 6, 9, 11].include?(month) 10 30 11 # elsif (month == 2) 12 elsif month.equal(2) 13 28 14 end 15 else # .... 16 # .... 17end
- 判定内容に名前を付けてあげます(この段の命名はもうすこし考えたほうがいいとは思います...)
lang
1def days31?(month) 2 [1, 3, 5, 7, 8, 10, 12].include?(month) 3end 4 5def days30?(month) 6 [4, 6, 9, 11].include?(month) 7end 8 9def days28?(month) 10 month.equal?(2) 11end 12 13def days29?(month) 14 days28?(month) 15end 16 17def days(year, month) 18 if Date.leap?(year) 19 if day31?(month) 20 31 21 elsif days30?(month) 22 30 23 elsif days28?(month) 24 28 25 end 26 else 27 if days31?(month) 28 31 29 elsif days30?(month) 30 30 31 elsif days29?(month) 32 29 33 end 34 end 35end
- ひとつ前の書き換えで、フクザツに見えていた重複点が見易くなりました。ここでさらに、
case when
に書き換えてみます
lang
1require 'date' 2 3def days(year, month) 4 case month 5 when 1, 3, 5, 7, 8, 10, 12 6 30 7 when 4, 6, 9, 11 8 31 9 when 2 10 Date.leap?(year) ? 28 : 29 11 end 12end 13 14def display_day_of_month 15 puts '年を入力してください' 16 year = gets.to_i 17 puts '月を入力してください' 18 month = gets.to_i 19 days = days(year, month) 20 puts "#{year}年の#{month}月は#{days}日です。" 21end
以上で、コードがだいぶスッキリした見た目になったと思います
長くなりましたが:
- フクザツな処理(ここでの、うるう年判定)はライブラリにお任せする
- 変数のみならず、様々なものに名前を付けてあげる(あるいは解説コメントを付ける)
- 関数は10行前後に留める
という点を意識して、ここまで書き換えてみました
Links
投稿2014/11/24 19:22
総合スコア2321
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
... ruby の学習という点では、自分でいろいろな条件判定を行うコードを書くのがよいのですが ...
ライブラリーに頼らず、自分で条件判定をするようなコードを書いてみました。
utils.rb と 動作の簡易チェックのための utils_test.rb の2つを以下に示します。
(test も rspec を使わずに書いてみました。
yard などで API ドキュメントを生成した時に、適切なドキュメントが生成されうようなコメントを
いれることもしなければいけまsんが、今回はそこはサボりました)
$ rubocop utils.rb でのコードスタイルのチェックでも 変な警告は出ないようにしてあります。
(gem install rubocop すると rubocop コマンドが使えるようになります)
utils.rb
lang
1# coding: utf-8 2 3# 便利メソッドを定義する。 4module Utils 5 # 閏年でないときの各月の日数 6 NORMAL_DAYS = { 7 1 => 31, 2 => 28, 3 => 31, 4 => 30, 5 => 31, 6 => 30, 8 7 => 31, 8 => 31, 9 => 30, 10 => 31, 11 => 30, 12 => 31 9 }.freeze 10 11 # year 年 month 月の日数を求める 12 def days(year, month) 13 check_year_month(year, month) # year, month の値範囲をチェックする。 14 15 # 閏年の2月は 29 日まである。 16 if month == 2 && leap?(year) 17 29 18 else 19 NORMAL_DAYS[month] 20 end 21 end 22 23 # year が閏年かを調べる。 24 def leap?(year) 25 (year % 4 == 0) && ((year % 400 == 0) || (year % 100 != 0)) 26 end 27 28 # year, month の値範囲をチェックする。 29 def check_year_month(year, month) 30 if (year < 1900) || (2038 < year) || (month < 1) || (12 < month) 31 throw ArgumentError.new("#{year}, #{month}") 32 end 33 end 34end 35# --- End of File ---
utils_test.rb
lang
1# coding: utf-8 2 3require File.join(File.expand_path(File.dirname(__FILE__)), 'utils.rb') 4 5#--------- test ------ 6include Utils 7 8TESTS = [ 9 [2000, 1, 31], 10 [2000, 2, 29], 11 [2000, 3, 31], 12 [2000, 4, 30], 13 [2000, 5, 31], 14 [2000, 6, 30], 15 [2000, 7, 31], 16 [2000, 8, 31], 17 [2000, 9, 30], 18 [2000, 10, 31], 19 [2000, 11, 30], 20 [2000, 12, 31], 21 22 [2001, 1, 31], 23 [2001, 2, 28], 24 [2001, 3, 31], 25 [2001, 4, 30], 26 [2001, 5, 31], 27 [2001, 6, 30], 28 [2001, 7, 31], 29 [2001, 8, 31], 30 [2001, 9, 30], 31 [2001, 10, 31], 32 [2001, 11, 30], 33 [2001, 12, 31] 34] 35 36TESTS.each do |t| 37 ans = Utils.days t[0], t[1] 38 fail "#{t.join(' ')} != #{t[0]} #{t[1]} #{ans}" unless ans == t[2] 39end 40 41ERR_TESTS = [ 42 [nil, nil, "undefined method `<' for nil:NilClass"], 43 [2000, nil, "undefined method `<' for nil:NilClass"], 44 [nil, 1, "undefined method `<' for nil:NilClass"], 45 46 [1899, 12, 'uncaught throw #<ArgumentError: 1899, 12>'], 47 [2039, 1, 'uncaught throw #<ArgumentError: 2039, 1>'], 48 49 [2000, 0, 'uncaught throw #<ArgumentError: 2000, 0>'], 50 [2000, 13, 'uncaught throw #<ArgumentError: 2000, 13>'] 51 52] 53ERR_TESTS.each do |t| 54 begin 55 Utils.days t[0], t[1] 56 fail 'エラーが発生しませんでした。' 57 rescue => ex 58 raise ex.to_s + t.join(' ') if ex.to_s != t[2] 59 end 60end 61 62# $ rubocop days.rb 63# $ ruby days_test.rb
投稿2014/11/25 16:19
総合スコア22324
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。