「うるう年」を複数の言語で判定してみる

# うるう年の定義
>グレゴリオ暦では、次の規則に従って400年間に97回の閏年を設ける。
>- 西暦年が4で割り切れる年は(原則として)閏年。
>- ただし、西暦年が100で割り切れる年は(原則として)平年。
>- ただし、西暦年が400で割り切れる年は必ず閏年。

by [Wikipedia(閏年)](http://ja.wikipedia.org/wiki/%E9%96%8F%E5%B9%B4)

## この記事について
複数の言語を調べつつうるう年を判定する処理を作成してみました。
関数の宣言、真偽の定義、繰り返しの構文、戻り値の返し方の違いについて、少し頭の整理ができました。
- うるう年の判定と数え上げの2つの関数(メソッド)を作り、なるべく同じ構成にする
- 判定は、年数を「400で割り切れる」または、「4で割り切れる」かつ「100で割り切れない」とする
- うるう年を1年から2024年まで数え上げ(491になる)、標準出力またはそれに準ずる場所に出力する

(各言語について、調べつつ記載しておりますので、間違っている可能性があります。)

## JAVA
```java
class YearUtils {
    private YearUtils() {}
    // うるう年判定処理
    public static boolean isLeapYear(int year) {
        return year % 400 == 0 || (year % 100 != 0 && year % 4 == 0);
    }
    // うるう年の件数取得処理
    public static int countLeapYear(int fromYear, int toYear) {
        int count = 0;
        for (int i = fromYear; i <= toYear; i++) {
            if (isLeapYear(i)) count++;
        }
        return count;
    }
    // うるう年の件数を表示
    public static void main(String[] args) {
        System.out.println(countLeapYear(1, 2024));
    }
}
```
条件判断はbooleanの true/false のみ。

値を返す関数の場合、returnは省略不可。

## JavaScript
```javascript
// うるう年判定処理
function isLeapYear(year){
    return year % 400 == 0 || (year % 100 != 0 && year % 4 == 0);
}
// うるう年の件数取得処理
function countLeapYear(fromYear, toYear){
    var count = 0;
    for (var i = fromYear; i <= toYear; i++) {
        if (isLeapYear(i)) count++;
    }
    return count;
}
// うるう年の件数を表示
WScript.Echo(countLeapYear(1, 2024))
```
条件判断は、 [ false, null, undefined, 0, NaN, 空文字列 ]が偽。それ以外の全てが真。

returnを省略した場合、戻り値は返されない。

## ExcelVBA
```ExcelVBA
'うるう年判定処理
Function isLeapYear(ByVal year As Integer)
  isLeapYear = year Mod 400 = 0 Or (year Mod 100 <> 0 And year Mod 4 = 0)
End Function
'うるう年の件数取得処理
Function countLeapYear(ByVal fromYear As Integer, ByVal toYear As Integer)
  Dim count As Integer
  Dim i As Integer
  For i = fromYear To toYear
    If (isLeapYear(i)) Then count = count + 1
  Next i
  countLeapYear = count
End Function
'うるう年の件数を表示
Sub Exec()
  MsgBox countLeapYear(1, 2024)
End Sub
```
条件判断は、 [ False, 0 ]が偽。[ True, 0以外の数値 ]は真。 

return文は存在しない。処理を中断したい場合はEXITを使う。
関数の戻り値を返す場合、関数と同じ名前の変数に値を代入する。 

# python
```python
# うるう年判定処理
def isLeapYear(year):
    return year % 400 == 0 or (year % 4 == 0 and year % 100 != 0)

# うるう年の件数取得処理
def countLeapYear(fromYear, toYear):
    n = 0
    for i in range(fromYear, toYear+1):
        if isLeapYear(i):
            n += 1
    else:
        return n

# 出力
print(countLeapYear(1, 2024))
```
注: pythonについて調べつつ書いたものになります。拙いコードですみません。

変数を使用する時に事前に使用する変数を宣言したり、変数で扱うデータ型を指定する必要はない。
ただし定義されていない変数の値を参照しようとするとエラーが発生する。

条件判断はbooleanの true/false　で行う。

returnを省略した場合、「None」が戻り値になる。

# Ruby
```Ruby
# うるう年判定処理
def isLeapYear(year)
    year % 400 == 0 || (year % 100 != 0 && year % 4 == 0) 
end
# うるう年の件数取得処理
def countLeapYear(fromYear, toYear)
    count = 0
    for i in fromYear..toYear do
        if isLeapYear(i)
            count += 1
        end
    end
    return count
end
# 出力
puts countLeapYear 1, 2024
```
注: Rubyについて調べつつ書いたものになります。拙いコードですみません。

変数はオブジェクトが格納されている場所を格納しています。
変数には値が格納されるわけではないので変数そのものにはデータ型はありません。

条件判断は、 [ false, nil ]が偽、それ以外が真。
nilはnullの別名みたいなもの。

returnを省略した場合、最後に評価された式の結果が戻り値になる。
意味が明確であれば、引数の括弧は省略可能。

# シェルスクリプト
```shell
#!/bin/sh

# うるう年判定処理
isLeapYear(){
    return $((!($1 % 400 == 0 || ($1 % 100 != 0 && $1 % 4 == 0))))
}
# うるう年の件数取得処理
countLeapYear(){ 
    local toYear=$2

    local count=0
    local i=$1

    while [ $i -le $toYear ]; do
        if isLeapYear $i; then
            count=$(($count+1))
        fi
        i=$(($i + 1))
    done

    echo $count
}
# 出力
echo `countLeapYear 1 2024`
```
変数から値を取り出すには「$変数」とする。
引数の文字列は空白で分割され、「$1」や「$2」として取り出せる。

条件判断は、 条件式のコマンドの「終了ステータス」が0の時に真、それ以外は偽。

「return」は戻り値を返すものではなく「終了ステータス」を返す。
0は正常終了、それ以外はエラー。よって条件判断も0の時だけ真となる。

# バッチファイル
```bat
@echo off

call :showResult 1 2024
exit /b

rem うるう年判定処理
:isLeapYear
setlocal
    rem 年が400で割り切れるとうるう年
    set /a rest=%1%%400
    if %rest%==0 exit /b 1
    rem 年が100で割り切れるとうるう年ではない
    set /a rest=%1%%100
    if %rest%==0 exit /b 0
    rem 年が4で割り切れるとうるう年
    set /a rest=%1%%4
    if %rest%==0 exit /b 1
    exit /b 0
endlocal

rem うるう年の件数取得処理
:countLeapYear
setlocal
    set fromYear=%2
    set toYear=%3
    set count=0
    for /l %%i in (%fromYear%,1,%toYear%) do (
        call :isLeapYear %%i || set /a count+=1
    )
endlocal & set %~1=%count% & exit /b 0

rem 出力
:showResult
setlocal
    set n=0
    call :countLeapYear n %1 %2
    echo.
    echo %n%
    exit /b
endlocal
```
変数から値を取り出すには「%変数%」となる。
引数の文字列は空白で分割され、「%1%」や「%2%」として取り出せる。
変数に値を代入する「set」コマンドに「/a」を付けると、引数の文字列を数式として評価してくれる。
その文字列の中で変数を使うことができ、値を代入することも可能となる。

条件判断は、よく分かんない。
「if」は条件の中で式を実行させるわけじゃないから、真偽とかそういう問題じゃない。
「if」に複数の条件を与えることは出来ない。andとかorとかいう概念が存在しない。

関数が存在しない。
処理は先頭から順番に実行され、ファイルの最後か「exit」コマンドに到達した時点で終了する。

行を「:」で始めることによってその行にラベルを貼り、「goto」や「call」でその行に移動できる。
「call」には引数を渡すことができ、「exit /b」によって呼び出し行まで戻れる。
これによって一応は関数のように使用できるが、戻り値は返せない。
「exit /b」を忘れるとラベルを無視して、そのまま処理が実行される。
戻り値ではないが、「exit /b」の後ろに数値を渡すことによってエラーレベルを返せる。

# 最後に
複数言語で「うるう年」を判定してみましたが、各言語で違いがあり勉強になりました。
以上、ありがとうございました。