cal

で、プログラムシンポジウムではなんか cal を書けというのがお題だったので会場で適当に書いてた。それ以外でも久々に好き勝手なコード書きまくれて楽しかった。

D言語。特にゴルフとかはしてない。

import std.metastrings;
template is_leap(int y) {
    const int is_leap = y % 400 == 0 || (y % 100 != 0 && y % 4 == 0);
}
template days(int y, int m) {
    const int days = (is_leap!(y) && m == 2) ? 29 : "X303232332323"[m]-20;
}
static assert(days!(2000, 2) == 29);
static assert(days!(2001, 2) == 28);
static assert(days!(2000, 3) == 31);
template wday(int y, int m) {
    const int wday = (y / 400 - y / 100 + y / 4 + y + "X144725736146"[m]
                          - 49 - is_leap!(y) * (m <= 2)) % 7;
}
static assert(wday!(2001, 3) == 4);
static assert(wday!(2001, 2) == 4);
static assert(wday!(2001, 1) == 1);
static assert(wday!(2000, 3) == 3);
static assert(wday!(2000, 2) == 2);
static assert(wday!(2000, 1) == 6);
template spaces(int n) {
    static if (n == 0) const char[] spaces = "";
    else const char[] spaces = " " ~ spaces!(n-1);
}
template day_str(int d) {
    static if (d < 10) {
        const char[] c = " " ~ ToString!(d);
    }
    else {
        const char[] c = ToString!(d);
    }
}
template weeks(int d, int m, int w) {
    static if (d > m) const char[] weeks = "";
    else {
        const char[] weeks =
            day_str!(d).c ~ " \n"[w%7==6] ~ weeks!(d+1, m, w+1);
    }
}
template cal_main(int y, int m) {
    const int w = wday!(y, m);
    const char[] c = spaces!(w*3) ~ weeks!(1, days!(y, m), w);
}
template cal(int y, int m) {
    const char[] cal = "Su Mo Tu We Th Fr Sa\n" ~ cal_main!(y, m).c;
}
const char[] c = cal!(2007, 8);
pragma(msg, c);

Befunge は id:mayah さん長くなりすぎだっ…とか勝手に対抗心を燃やして適当に作った。そんなに短くもない。あと p と g 封印しておられたらしいのでそもそも。

v144725736146
0&#&%&%&&%&%&
>25*"aS rF hT eW uT oM uS">:#,_&v
v+g0p00:&--+/4:/**455:/***2558::<
>\:::554**%!\4%+\8552***%*!\v
v:%7-p12+g12*!-2g00:*!`2g00$<
>v  >1-"   ",,,
v>:#^_
>1+:: 9`!#v_ #vv#  " "\_v
 52*,$0\:v>" ",>.\1+:7%^>
|--7g1g00<:   <
>52*,@

Brainfuck 。最近チラホラと「弊社でも Brainfuck の導入を検討しているのだが、パフォーマンス面での不安がぬぐえない」というような話を聞くようになってきたのですが、 cal くらいなら一瞬 (うちの MacBook で C のインタプリタ使って ~0.01sec) で解ける言語らしいです。

>>>>>>>>>>>>>>>>>>>>>,--------------------------------
[---------------->,--------------------------------]
<+<[->++++++++++<]<<[->++++++++++<]<<<,
------------------------------------------------<,+
[-----------[-----------------------------[->+<]]]>[-<+<+>>>>+<<]<<
<<------<----<-<------<---<-------<-----<--<-------<----<----<-
[>]>-[-<<[<]>[+]>[>]>]-<<<[>[+]<<]>>+[-<[+>-<]>>+]>[-<<<<+>>>>]<<<<
<<-<--<-<--<-<-<--<-<--<-<----<-[>]>-[-<<[<]>[+]>[>]>]-
<<[>[+]<<]>>+[-<[+>-<]>>+]<++++++++++++++++++++++++++++++++>>>>>>>>
>>[-<+>>>>>>>+<<<<<<]>>>>>>
[>[-]>[-]>[-]>[-]<<<<<<++++<+>[->>[>]+[<]>-<<]>>]>[>]<<[>-<[<]>+[>]<<]
>[-<<<+>>>]<<<----[++++<->>]<[<]
>-[-<+>>>>>>>+<<<<<<]>>>>>>++++
[>[-]>[-]>[-]>[-]<<<<<<++++<+>[->>[>]+[<]>-<<]>>]>[>]<<[>-<[<]>+[>]<<]
>[-<<<+>>>]<<<----[++++<->>]<<<<<<<<<[<]>>>>>>>>-
[-<<+>>]<<<<[-<+<<<+>>>>]>>[-<<<+>>>]<<<[->+<]<[->+>+++++<<]>>
<<<<<+>[<->>>>>>+>>>>[<<<<->>>>[-]]<<<<<<<<<[-]]
<[->>>>>>+>>[<<->>[-]]<<<<<<<<]>>>>>
>[-<<<<------------[+[+[+[+[+[+[+[+[+[>>>-<<<
++[<<<<<<<<+>>>>>>>>-][+]]]]]]]]]]]>>>>]<
-<<<<<<<<[+>>>>>>>>+<<<<<<<<]>>>>>>>>[->>>>>>>>>+<<<<<<<<<]>>>>>>>>>
[>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<+++++++<+>
[->>[>]+[<]>-<<]>>]>[>]<<[>-<[<]>+[>]<<]>[-<<<+>>>]<<<<+>
-------[+++++++>>+<]>[<]>[-]<<[->+>+<<]>>
>++++++++++++++++++++++++++++++++<[->...<]>[-]<+<-------<[-]>
><<<<<<<<<<<<<<<<<<<<[>>>>>>>>>>>>>>>>>>>>
[->+>+>+<<<]>>>-[-[-[-[-[-[-[-[-[[-]<
[->>>+<<<]>>>[>>>>>>>>>>>>>>>>>>>>+<<<<<<<<<<<<<<<<<<<<
>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]>[-]<<<<<<<<<<<<++++++++++<+>
[->>[>]+[<]>-<<]>>]>[>]<<[>-<[<]>+[>]<<]
>----------[++++++++++<>>>>>>>>>>>>>>>>>>>>-
<<<<<<<<<<<<<<<<<<<<]<<<[>]>>>>>>>>>>>>>>>>>>>>>>
++++++++++++++++++++++++++++++++++++++++++++++++.[-]
<<<<<<<<<<<<<<<<<<<<>++++++++++++++++++++++++++++++++++++++++++++++++.
[-]<[-]<<<[-]]]]]]]]]]<[<]>>
[>++++++++++++++++++++++++++++++++.[-]<
++++++++++++++++++++++++++++++++++++++++++++++++.[-]]<<<
+[>>>++++++++++++++++++++++<<<<]>>[<<------->]
>>++++++++++.[-]<[-<+>]<+[->+>+<<]>[-<+>]>[-]<<<<<<<<<<<<<<<<<<<<<<-]

実行例。1ケタ2ケタ3ケタの年の対応をするのは忘れた。やればすぐできると思うけど。

i@um ~/wrk/ag> time (echo 2007 12 | ./BFI ~/cal.bf)
                   1
 2  3  4  5  6  7  8
 9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
(; echo 2007 12 | ./BFI ~/cal.bf; )  0.00s user 0.01s system 83% cpu 0.012 total

ゴルフとか修行僧みたいとか言われたんだけど、 Brainfuck 書いてる方がそんな感じな気もする。

適当に書いてムダだらけなのにそれなりに高速に動いてる理由はたぶんアルゴリズムが良かったのかなと思う。 Brainfuck のセルって 256 までしか入らないから、2000年とかを表現できないんですよね。んでそれだと相当ムリゲーだなーと思ってたのですが、喋ってて2ケタずつ保持すればいいと気付いたのでした。

つまり 2007 なら 20 と 7 を持って、それぞれ 4 で割った結果と 4 で剰余した結果を用意しておくと、 cal に必要なものは全て計算できるのであった。

y1y2 で y1 に上2ケタが入ってると考えると、うるう年かどうかの判定は、 y2 == 0 ? y1%4 == 0 : y2%4 == 0 。

1月1日の wday は、 Y/400 - Y/100 + Y/4 + Y を 7 で剰余取ればいいんだけど、

(Y/400 - Y/100 + Y/4 + Y) % 7
(y1/4 - y1 + (y1*25+y2/4) + (y1*100+y2)) % 7
(y1/4 - y1 + (y1*4+y2/4) + (y1*2+y2)) % 7
(y1/4 + y1*5 + y2/4 + y2) % 7

と変形するとうまいこといった。25=>4とかの変形は mod 7 なので。

なにかあれば下記メールアドレスへ。
shinichiro.hamaji _at_ gmail.com
shinichiro.h