日々之迷歩

世の中わからんことだらけ

ITが複雑で難しくなっていく様に翻弄される日々です。微力ながら共著させていただいた「シェル・ワンライナー160本ノック」をよろしくお願い申し上げます。

閏年をデータ重視な発想で

みなさん閏年が何か知ってるよね???

・・・ええ勿論ご存知だろう。詳しくはウィキペディアをご覧あれ。今採用されているのはグレゴリオ暦の法則。

閏年 - Wikipedia

法則は下記の通り。案外間違えやすいのではなかろうか。

  1. 西暦年が4で割り切れる年は閏年
  2. ただし、西暦年が100で割り切れる年は平年
  3. ただし、西暦年が400で割り切れる年は閏年

じゃあ閏年の列挙はどうやればいいのか???

よくやる考え方は、if文とかで各年が4で割り切れるか?100で割り切れるか??とかをやると思われる。そのようなやり方は他のWebでたくさん記載されていると思うので、もっと違う発想でやる方法を書いてみる。

UECのサイトに下記の記事が書いてあるのを思い出した。まずはデータを準備しておいて、データ同士の重複比較やマージを使うことで、if文などの制御構造無しでスッキリと処理が書けるということ。

UEC - usp engineers' community - UECジャーナル

つまり下記のデータを準備し、それらをcatsortuniqなどのコマンドで重複比較やマージすることで閏年を出してみることにする。

  1. 4で割り切れる年のデータ
  2. 100で割り切れる年のデータ
  3. 400で割り切れる年のデータ

これらのデータを準備して、1.のデータから2.のデータを排除し、さらに3.のデータをマージしてやればいいのだ。

では20世紀から24世紀までの閏年を列挙してみよう。まずは上記の3つのデータを作る。

$ seq 1901 2400 | awk '$1%4==0' > year4
$ seq 1901 2400 | awk '$1%100==0' > year100
$ seq 1901 2400 | awk '$1%400==0' > year400

$ head year*
==> year100 <==
2000
2100
2200
2300
2400

==> year4 <==
1904
1908
1912
1916
1920
1924
1928
1932
1936

==> year400 <==
2000
2400

では準備した3つのデータを元に、グレゴリオ暦の閏年法則に則って処理してみる。

# 1. 西暦年が4で割り切れる年は閏年
$ cat year4
1904
1908
1912
1916
1920
1924
1928
1932
1936
....
2364
2368
2372
2376
2380
2384
2388
2392
2396
2400

# 2. ただし、西暦年が100で割り切れる年は平年
$ cat year4 | cat - year100 | sort | uniq -u
1904
1908
1912
1916
1920
1924
1928
1932
1936
1940
....
2360
2364
2368
2372
2376
2380
2384
2388
2392
2396

# 3. ただし、西暦年が400で割り切れる年は閏年
$ cat year4 | cat - year100 | sort | uniq -u | cat - year400 | sort | uniq
1904
1908
1912
1916
1920
1924
1928
1932
1936
1940
....
2364
2368
2372
2376
2380
2384
2388
2392
2396
2400

ということで、データ重視なアプローチで閏年を列挙した。

ちなみに上記の処理をシェル芸なワンライナーだとこんな感じになる。上記のようなデータ重視なアプローチがあってこそのワンライナー。

$ seq 1900 2400 | awk '$1%4==0' |
> ( cat; seq 1900 2400 | awk '$1%100==0' ) | sort | uniq -u |
> ( cat; seq 1900 2400 | awk '$1%400==0' ) | sort | uniq