質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.34%

  • Ruby

    10231questions

    Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

  • if

    278questions

    if文とは様々なプログラミング言語で使用される制御構文の一種であり、条件によって処理の流れを制御します。

  • while

    94questions

    Whileは多くの言語で使われるコントロール構造であり、特定の条件が満たされる限り一連の命令を繰り返し実行します。

Rubyで『ある年のある月の日数を求めるメソッド』を作成したのですが...

解決済

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 1,353

Kei227

score 34

閏年かどうかの判定のために以下の条件を設けて、『ある年のある月の日数を求めるメソッド』をRubyで作成しました。

-閏年の条件-
① その年が4で割り切れること
② ただし、年が100で割り切れて400で割り切れない場合は閏年ではない


def get_days(year, month)
  month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
  if month == 2
    if year % 4 == 0
      if (year % 100 == 0) && (year % 400 != 0)
        days = 28
      else
        days = 29
      end
    else
      days = 28
    end
  else
    days = month_days[month - 1]
  end

  return days
end

while true do
  puts "年を入力してください:"
  year = gets.to_i
  puts "月を入力してください:"
  month = gets.to_i

  days = get_days(year, month)
  puts "#{year}年#{month}月は#{days}日間あります"
end


メソッドの最後に書いた【return days】は必要ないでしょうか?入れても入れなくても結果は変わらなかったので必要ないかと思ったのですが、模範解答にはこの記述がありましたので気になります。

また、改善点などもございましたら合わせてご教授願います。
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

+1

return の有無についての説明はすでに説明がありましたが、
質問文のコード自体についてコメントがあります。

ある年のある月の日数を求めるのを3つの方法で書いてみました。
  1. 質問文のコードを変更したもの
  2. Date#leap? メソッドを使ったもの
  3. Date のイニシャライザを使ったもの

質問文のコードについては次の点を変更しています。
  * month_datas は 定数にして メソッドの外の出して、一度だけ生成するようにした。
  * うるう年の判定をメソッドとして独立させた。
  * while true do は loop do に置き換えた。

require 'date'

MONTH_DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31].freeze

def get_days(year, month)
  days = MONTH_DAYS[month - 1]
  days = 29 if month == 2 && Date.leap?(year)
  days
end

def leap_year?(year)
  ans = false
  if year % 400 == 0
    ans = true
  elsif year % 4 == 0 && year % 100 != 0
    ans = true
  end
  ans
end

def get_days2(year, month)
  days = MONTH_DAYS[month - 1]
  days = 29 if month == 2 && leap_year?(year)
  days
end

loop do
  puts '年を入力してください:'
  year = gets.to_i
  puts '月を入力してください:'
  month = gets.to_i

  days = get_days(year, month)
  days2 = get_days2(year, month)
  days3 = Date.new(year, month, -1).day
  puts "#{year}年#{month}月は#{days}, #{days2}, #{days3}日間あります"
end
実行例:
$ ruby 1.rb
年を入力してください:
2000
月を入力してください:
2
2000年2月は29, 29, 29日間あります
年を入力してください:
2004
月を入力してください:
2
2004年2月は29, 29, 29日間あります
年を入力してください:
2100
月を入力してください:
2
2100年2月は28, 28, 28日間あります

↑のコードは rubocop で警告はでません。
(質問文のコードはいくつか警告がでます)

参考情報:
- 月末の日付を取得する http://qiita.com/wada811/items/774afd371c4257ab613e
...
Date.new の第3引数に負の数を指定すると、月末からの日付で初期化できます。
-1 を指定すれば月末の日付を取得できます。
...

- Ruby 2.2.0 リファレンスマニュアル > ライブラリ一覧 > dateライブラリ > Dateクラス http://docs.ruby-lang.org/ja/2.2.0/class/Date.html#S_GREGORIAN_LEAP--3F

追記: 2015-11-16 23:30
rubocop の警告の例;
イメージ説明

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/11/16 22:35

    より良いコードへの訂正ありがとうございます。


    * while true do は loop do に置き換えた。

    こちらの変更は、breakで処理から受け出すためでしょうか?(whileだと永遠に抜けられないので)


    また、whileとloapはどのように使い分けていけばいいかなどのアドバイスがございまいしたら教えていただきたいです。

    キャンセル

  • 2015/11/16 23:11

    【追記】
    Sublime Textに教えていただいたrubocopを適応させようと思い、以下のマニュアル通りに実行したのですが、
    =======
    Manual:
    Navigate to the Sublime Text Packages folder (You can find the location of the Packages folder here).

    Run the git clone command right inside the packages directory: git clone git@github.com:pderichs/sublime_rubocop.git "RuboCop"

    Restart Sublime Text.
    =======


    エラーが発生してしまいます。
    =======
    MBP:~ k$ cd ~/Library/Application\ Support/Sublime\ Text\ 3/Packages/
    MBP:Packages k$ git clone git@github.com:pderichs/sublime_rubocop.git "RuboCop"
    Cloning into 'RuboCop'...
    Warning: Permanently added the RSA host key for IP address '192.30.252.130' to the list of known hosts.
    Permission denied (publickey).
    fatal: Could not read from remote repository.

    Please make sure you have the correct access rights
    and the repository exists.
    =======


    調べてみると下記の記述を見つけたので、
    =======
    ■原因
    git remote add したリポジトリのパスが間違ってる可能性が高い。
    ■対策
    git remote rmしてパスを確認して、間違っているならaddし直す。
    =======

    『git remote rm』を実行して対処しようと試みたのですが、エラーが出てしまいます。
    =======
    MBP:Packages k$ git remote rm
    fatal: Not a git repository (or any of the parent directories): .git
    =======


    どのようにすれば無事SublimeでRuboCopを使えるようになりますでしょうか?

    キャンセル

  • 2015/11/16 23:34

    loop do でも while true でも break でループから脱出はできます。
    回答のほうに追記しましたが、
    rubokop ( atom 上で動作させています) で警告がでるので、それを解消させたのです。

    subline は使っていないので、わかりません。
    rubocop はコマンドラインでもつかえます。(エディタと連動させたほうが便利だけど)

    キャンセル

checkベストアンサー

0

Rubyのメソッドではreturn文が無い場合、最後に評価された式の値が返されます。そして、Rubyではifは式であり、最後に評価された式の値が返されます。なので、return daysがなくても結果が変わらないのです。

もうちょっと、細かく説明していきます。return daysが無かった場合を考えます。一番最後の式は、if month == 2から始まってそれに対応するendです。つまり、メソッドの返り値としてこの式の値が返されます。では中身を見ると、もし、monthが2の場合はif year % 4 == 0から始まってそれに対応するendが最後の式、2ではない場合はdays = month_days[month - 1]が最後の式です。if year % 4 == 0をみるとさらにifが入れ子になっていて、最終的に全てdays = 【何か】という代入式が最後に評価された式になります。つまり、days = 【何か】を式として評価したときに、返す値がそのまま各ifでも返す値になり、最終的にメソッドが返す値になると言うことです。そして、代入式は単純に代入された値を返します。結局の所、daysに代入された値がメソッドの返り値になっているのです。なので、return daysが無かったとしても、いまのコードではどのif分岐を辿ってもdaysへの代入で終わるので、ちゃんとした値が返ると言うことです。return daysと各所のdays =を削除して、値だけにしても、同じになることが確認できると思います。

では、return daysを消しちゃっていいかというとそうではありません。もし、return daysが無ければ、全てのif分岐について最後に評価される式の値が返り値としたいかを確認する必要があるからです。ちょっとかえただけでうまく値を返さずに、わかりにくいバグのもとになります。たとえば、デバッグ用にdays = 28の後だけputs "#{year} is a leap year!"とかつけたらどうでしょう?閏年の時だけputsの式が最後になって、おかしくなってしまいます。いいコードというのは、こういった変更に強い(全体を変えなくても部分的な変更ができる)コードです。ですので、模範解答はreturn daysをつけて、部分的な変更があっても意味が変わらないようにしているのです。

上を読み返してみると、自分でも何言っているのかわかりませんでした。

Rubyの仕様上、returnが無くてもこのコードは動くけど、説明がちょっと複雑だから、わかりやすいコードになるようにちゃんとreturnを書こうね!

って、覚えておけば大丈夫っす!

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/11/16 21:30

    returnをつけることで返り値を明確にし、部分的な変更があってもメソッドの意味を変えないのですね。


    『全てのif分岐について最後に評価される式の値が返り値としたいかを【確認する必要がある】からです。』

    の【確認する必要がある】というのは何か具体的な動作を指すのでしょうか?

    キャンセル

  • 2015/11/16 21:36 編集

    if分岐の一つ一つについて、最後に何の式を評価しているのかを**プログラマー**が目視で【確認する必要がある】ということです。ifが1段階だけであればそれほど難しくないですが、今回のように3段階ぐらいネストしてしまうと結構大変だと思います。それよりは、それぞれでdaysに入れるという明確な処理と、最後にdaysを返すってしたほうが、理解もしやすい=いいコードということなのです。

    キャンセル

  • 2015/11/16 21:43

    理解できました。誰が見ても意図通りに理解される分かりやすいコードを書くことを心がけます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.34%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • Ruby

    10231questions

    Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

  • if

    278questions

    if文とは様々なプログラミング言語で使用される制御構文の一種であり、条件によって処理の流れを制御します。

  • while

    94questions

    Whileは多くの言語で使われるコントロール構造であり、特定の条件が満たされる限り一連の命令を繰り返し実行します。