わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

馬齢も

続・満年齢の計算 - わさっきの続きです.

馬齢(ばれい)とは馬の年齢のことである。
日本では生まれた時が0歳で、以後1月1日が来ると同じ年に生まれた馬は一斉に1歳加齢する。

馬齢 - Wikipedia

国による違いや,日本でもこの算出方法にしたのは2001年からだとかありますが,そういうのは省略させてもらって,上の意味での馬齢を求める処理を,以前のコードに追加しましょう.
出来上がりはこんなの.ちなみに前回のファイル名はage2.rb,今回はage3.rbです.

#!/usr/bin/env ruby

require 'date'

class Age
  METHOD_ORDINAL = 1    # 誕生した日を1歳とし,1月1日に年齢を1加える
  METHOD_CONVENTION = 2 # 誕生日に年齢を1加える
  METHOD_LAW = 3  # 年齢計算ニ関スル法律に基づき,誕生日の前日に年齢を1加える
  METHOD_HORSE = 4      # 誕生した日を0歳とし,1月1日に年齢を1加える(馬齢)

  def initialize
    @birth = nil # 生年月日.Dateインスタンスまたはnil
    @today = Date.today # 基準日
    @method = METHOD_CONVENTION # 算出方法.定数METHOD_*のいずれか
    @readiness = 0 # 入力データの状況
  end
  attr_accessor :birth, :today, :method
  attr_reader :readiness

  def parse(argv)
    @readiness = 0

    # コマンドライン引数解析ができなければすぐ終了
    return if !(Array === argv) or argv.empty?

    @readiness = 1

    while !argv.empty?
      # -l, -o, -c オプションの判定
      if argv[0][0, 1] == '-'
        param = argv.shift
        case param
        when '-o'
          @method = METHOD_ORDINAL
        when '-c'
          @method = METHOD_CONVENTION
        when '-l'
          @method = METHOD_LAW
        when '-h'
          @method = METHOD_HORSE
        end
      else
        d, argv = create_date(argv)
        return unless d
        case @readiness
        when 1
          # 生年月日
          @birth = d
          @readiness = 2
        when 2
          # 基準日
          @today = d
          @readiness = 3
        end
      end
    end
  end

  def create_date(argv)
    d = nil

    begin
      if argv.size >= 3 and /^\d+ \d+ \d+$/ =~ argv[0, 3].join(' ')
        # 3つの数値が引数にあるなら,それをDateオブジェクトに変換する
        d = Date.parse(argv.slice!(0, 3).join('/'))
      else
        # そうでなければ,配列の最初の要素をDateオブジェクトに変換する
        d = Date.parse(argv.shift)
      end
    rescue
      # 失敗したら,nilと,日付取得のための要素を取り除いた配列を返す
      # (無限ループ回避のため)
    end

    return d, argv
  end
  
  def method_to_s
    case @method
    when METHOD_ORDINAL
      'ordinal'
    when METHOD_LAW
      'by law'
    when METHOD_HORSE
      'house age'
    else
      'by convention'
    end
  end

  def calc_age
    # 未来の人の年齢を求めることはできない
    return -1 if @today < @birth

    # 年差を仮の年齢とおく
    age = @today.year - @birth.year

    case @method
    when METHOD_ORDINAL
      # 数え年: 年差+1
      age += 1
    when METHOD_HORSE
      # 馬齢:今のageの値
    else
      # [基準年, 生まれ月, 生まれ日]をもとにDateインスタンスを作る.
      # ただし,2月29日生まれで,基準年が閏年でない場合は,
      # 基準年の3月1日を用いる.
      if !@today.leap? and @birth.mon == 2 and @birth.mday == 29
        d = Date.new(@today.year, 3, 1)
      else
        d = Date.new(@today.year, @birth.mon, @birth.mday)
      end
      # 法律の満年齢: 年差 (ただし,誕生日の前日より前なら -1)
      # 慣習の満年齢: 年差 (ただし,誕生日より前なら -1)
      d -= 1 if @method == METHOD_LAW
      age -= 1 if @today < d
    end

    # 戻り値
    age
  end
end

if __FILE__ == $0
  # 引数チェック
  ac = Age.new
  ac.parse(ARGV)
  case ac.readiness
  when 0
    puts "Usage: ruby #{$0} [-h][-l][-c][-o] birthdate [today]"
    exit
  when 1
    puts "Wrong parameter"
    exit
  end

  # 年齢計算
  age = ac.calc_age

  # 出力
  puts "Birth date: #{ac.birth}"
  puts "Today:      #{ac.today}"
  if age < 0
    puts "Preborn?"
    exit
  end
  puts "Age: #{age}"
  puts "Method: #{ac.method_to_s}"
end

数え年から1を引けばいいや,という以前に,単純に年差でよかったのでした.
さて,実行結果.

$ ruby age3.rb 2000 2 6
Birth date: 2000-02-06
Today:      2008-02-24
Age: 8
Method: by convention
$ ruby age3.rb -l 2000 2 6
Birth date: 2000-02-06
Today:      2008-02-24
Age: 8
Method: by law
$ ruby age3.rb -o 2000 2 6
Birth date: 2000-02-06
Today:      2008-02-24
Age: 9
Method: ordinal
$ ruby age3.rb -h 2000 2 6
Birth date: 2000-02-06
Today:      2008-02-24
Age: 8
Method: house age
$ ruby age3.rb 2000/2/29 2003/3/1
Birth date: 2000-02-29
Today:      2003-03-01
Age: 3
Method: by convention
$ ruby age3.rb -o 2000/2/29 2003/3/1
Birth date: 2000-02-29
Today:      2003-03-01
Age: 4
Method: ordinal
$ ruby age3.rb -h 2000/2/29 2003/3/1
Birth date: 2000-02-29
Today:      2003-03-01
Age: 3
Method: house age

ついでに,前のと差分を出しておきましょう.

$ diff age2.rb age3.rb
8a9
>   METHOD_HORSE = 4      # 誕生した日を0歳とし,1月1日に年齢を1加える(馬齢)
37a39,40
>         when '-h'
>           @method = METHOD_HORSE
80a84,85
>     when METHOD_HORSE
>       'house age'
93c98,99
<     if @method == METHOD_ORDINAL
---
>     case @method
>     when METHOD_ORDINAL
95a102,103
>     when METHOD_HORSE
>       # 馬齢:今のageの値
116c124
< if __FILE__ == $0 and false
---
> if __FILE__ == $0
122c130
<     puts "Usage: ruby #{$0} [-l][-c][-o] birthdate [today]"
---
>     puts "Usage: ruby #{$0} [-h][-l][-c][-o] birthdate [today]"

特に注意することはないのですが,まあ,メソッドcalc_ageの中で@methodで場合分けする際に,case〜whenに置き換えています.この値がMETHOD_HORSEのときは何も処理しませんが,「when METHOD_HORSE」を書いておかないとelseの処理をしてしまいます.あと,Cのswitch〜caseと違ってbreakは不要です.いや,書くとエラーです.