Panda Noir

JavaScript の限界を究めるブログです。

うるう年の判定王を決めよう

(この記事はQiitaで僕が書いたものを移行した記事です。記事中のコメントはQiitaの該当記事を参照ください)

今年2016年はうるう年です。2月は29まであり1年が366日でオリンピックが開かれます。

そして、うるう年判定はプログラマにとって熱いものがこみ上げますよね。縛りを入れるとなかなか難易度がちょうど良くなり楽しいです。

まず参加エントリーNo. 1の僕のコードから

オーソドックスなやつ

まずは縛りなし

function isLeapYear(y) {
    y = Number(y);
    return y % 400 === 0 || y % 100 !== 0 && y % 4 === 0;
}

簡潔ですね。

剰余(%)縛り

次は剰余なしで

function isLeapYear(y) {
    y = Number(y);
    const dividedBy4 = (y >> 2 << 2) === y;

    const dividedBy100 = (0 | y / 100) * 100 === y;

    const dividedBy400 = dividedBy100 && (0 | y / 100) >> 2 << 2 === (0 | y / 100);

    return dividedBy400 || !dividedBy100 && dividedBy4;
}

整数に丸めて変わらないかで判定しています。(dividedByFourとかいう文法ガン無視の変数名は気にしないでください)

さらに「整数に丸める操作」縛り

Math.floorやparseIntといった整数に丸める処理を縛ります。もちろんここで>>といったビット演算も縛り対象となります。

そろそろキツイ…

function isLeapYear(y) {
    y = Number(y);
    let dividedBy4 = y.toString(2).slice(-2);
    dividedBy4 = dividedBy4 === '00' || dividedBy4 === '0';

    let dividedBy100 = y.toString(10).slice(-2);
    dividedBy100 = dividedBy100 === '00' || dividedBy100 === '0';

    let dividedBy400 = Number(y.toString(10).slice(0, -2)).toString(2).slice(-2);
    dividedBy400 = dividedBy100 && (dividedBy400 === '00' || dividedBy400 === '0');

    return dividedBy400 || !dividedBy100 && dividedBy4;
}

うーん、暗黒ですね。一応解説しますと4の倍数かは2進法に変換後、下2桁が00かどうかで判断しています。100の倍数かは10進法で下2桁が00か、400の倍数かは10進法で下2桁を除いた数が4の倍数か + 100の倍数かで判定しています。

dividedByFourはこうも直せます。

    const dividedByFour = '13579'.indexOf(y.slice(-2, -1)) !== -1 && '26'.indexOf(y.slice(-1)) !== -1 || ('24680'.indexOf(y.slice(-2, -1)) !== -1 || y.slice(-2, -1) === '') && '048'.indexOf(y.slice(-1)) !== -1;

「400進法」

…400進法で下1桁が0ならば400の倍数じゃないか…!!と閃いたのですがJavaScriptのtoStringは32進法までしか対応してなかったので20進法に直した時下2桁が0かで判定にしたいと思います。まさか20進法なんて使う日が来るとは…

function isLeapYear(y) {
    y = Number(y);
    let dividedBy4 = y.toString(2).slice(-2);
    dividedBy4 = dividedBy4 === '00' || dividedBy4 === '0';

    let dividedBy100 = y.toString(10).slice(-2);
    dividedBy100 = dividedBy100 === '00' || dividedBy100 === '0';

    let dividedBy400 = y.toString(20).slice(-2);
    dividedBy400 = dividedBy400 === '00' || dividedBy400 === '0';

    return dividedBy400 || !dividedBy100 && dividedBy4;
}

きたれ、真のうるう年判定王!!

自分に課した縛り + コードという形で募集します。言語は自由です。

Rubyあたりはキチガイコードがバンバン出そうですね…