【D言語】逆FizzBuzz問題を変態的に
逆FizzBuzzというのがTwitterで流れてきたので解いてみました。
原文の日本語訳 http://d.hatena.ne.jp/matarillo/20120515/p1
FizzBuzzというのは、簡単にいえば世界のナベアツ(現在は桂三度)のネタみたいな感じです。
ちゃんと説明すると、複数人で1から順番に数を言っていきます。
しかし、すこし条件があり、
・3の倍数の場合にはその数の代わりにfizz
・5の倍数の場合にはその数の代わりにbuzz
・3と5の倍数、すなわち15の倍数の時にはfizzbuzz
と言わないといけません。つまり、
1, 2, fizz, 4, buzz, fizz, 7, 8, fizz, buzz, 11, fizz, 13, 14, buzz, 16, 17....
という感じで続いていきます。
その人がプログラムが本当に書けるかどうかを判定するために使われることが多いようです。
今回はそのFizzBuzzの逆、つまりfizz, buzz...という入力から数列を見つけてきます。
詳しくは原文の日本語訳をお読みください。
さて、FizzBuzzは3の倍数, 5の倍数, 15の倍数に関するものです。
ですから、周期が15の周期関数であると考えることもできます。
つまり、15までについて判定すればよいということになります。
この解き方自体はみなさん簡単に思いつくんではないでしょうか。
他人が簡単に思いつくような解き方を綺麗サッパリ書いても面白く無いので、今回はD言語で";"を関数内にただ一つだけ含むinvFizzBuzz関数を書いてみました。
「";"を関数内にただ一つだけ含む」という意味は、invFizzBuzz関数が次のような形式であるということを意味します。
auto invFizzBuzz(){
return ....... ;
}
よって、importによる";"や、main関数
void main(){
invFizzBuzz;
}
の";"は個数には含みません。
「どう解く」か、より、「どう書く」かが難しくなりますが、
ラムダ/UFCS/Range
を大変活用して書くことによりなんとか。
ソースコードは以下になりますが、結果として大変変態なコードになりました。
Ideone : http://ideone.com/AnYfo
入出力はこんな感じになります。
入力 → 出力
fizz → [3]
buzz → [5]
fizz buzz → [9, 10]
buzz fizz → [5, 6]
buzz buzz → []
fizz buzz fizz fizzbuzz fizz buzz fizz → [9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]
fizzbuzz fizzbuzz → []
このように最短解をちゃんと出力します。
結果的にラムダとUFCS,Rangeを多用した変態コードになりましたが、
別にD言語が変態というわけではなく、
D言語だと綺麗サッパリ書くことも出来ます。
ですから、この記事のソースコードをみて
D言語こわ...ちかよらんとこ...
とかそんなことは思わないでください。
D言語は良心的な紳士言語です。
原文の日本語訳 http://d.hatena.ne.jp/matarillo/20120515/p1
FizzBuzzというのは、簡単にいえば世界のナベアツ(現在は桂三度)のネタみたいな感じです。
ちゃんと説明すると、複数人で1から順番に数を言っていきます。
しかし、すこし条件があり、
・3の倍数の場合にはその数の代わりにfizz
・5の倍数の場合にはその数の代わりにbuzz
・3と5の倍数、すなわち15の倍数の時にはfizzbuzz
と言わないといけません。つまり、
1, 2, fizz, 4, buzz, fizz, 7, 8, fizz, buzz, 11, fizz, 13, 14, buzz, 16, 17....
という感じで続いていきます。
その人がプログラムが本当に書けるかどうかを判定するために使われることが多いようです。
今回はそのFizzBuzzの逆、つまりfizz, buzz...という入力から数列を見つけてきます。
詳しくは原文の日本語訳をお読みください。
さて、FizzBuzzは3の倍数, 5の倍数, 15の倍数に関するものです。
ですから、周期が15の周期関数であると考えることもできます。
つまり、15までについて判定すればよいということになります。
この解き方自体はみなさん簡単に思いつくんではないでしょうか。
他人が簡単に思いつくような解き方を綺麗サッパリ書いても面白く無いので、今回はD言語で";"を関数内にただ一つだけ含むinvFizzBuzz関数を書いてみました。
「";"を関数内にただ一つだけ含む」という意味は、invFizzBuzz関数が次のような形式であるということを意味します。
auto invFizzBuzz(){
return ....... ;
}
よって、importによる";"や、main関数
void main(){
invFizzBuzz;
}
の";"は個数には含みません。
「どう解く」か、より、「どう書く」かが難しくなりますが、
ラムダ/UFCS/Range
を大変活用して書くことによりなんとか。
ソースコードは以下になりますが、結果として大変変態なコードになりました。
Ideone : http://ideone.com/AnYfo
import std.algorithm;
import std.functional;
import std.range;
import std.stdio;
import std.string;
import std.typecons;
void main(){
invFizzBuzz;
}
auto invFizzBuzz(){
return stdin.byLine.map!(a => a.split)
.map!(ln =>
(a => (a > 6) ? [] : ((b, s) =>
(c =>
(c.popFrontN(b), c)
)
(
recurrence!"a[n-1] + 1"(1)
.map!(c => tuple(c, (!(c % 3) ? "fizz" : "") ~ (!(c % 5) ? "buzz" : "")))
.filter!"a[1].length"
)
.take(s)
.map!"a[0]"
.array
)(a, ln.length)
)
(
iota(0, 7)
.map!(a =>
tuple(
iota(1, 16).map!q{(!(a % 3) ? "fizz" : "") ~ (!(a % 5) ? "buzz" : "")}
.array
.cycle
.zip(recurrence!"a[n-1] + 1"(1))
.filter!"a[0].length",
a
)
)
.map!(a => (a[0].popFrontN(a[1]), a))
.map!(a =>
tuple(
a[0].zip(ln).map!"a[0][0] == a[1]".reduce!"a && b",
a[1],
a[0].front[1],
a[0].take(ln.length).curry!(reduce!"max(a, b[1])", 0)
)
)
.chain([tuple(true, 7, 0, 0)])
.filter!"a[0]"
.reduce!((a, b) => (a[3] - a[2]) <= (b[3] - b[2]) ? a : (b[1] == 7 ? a : b))[1]
)
)
.map!(a => a.empty ? iota(0, 0) : iota(a[0], a[$-1] + 1))
.map!(a => (a.writeln, 0)).reduce!"a + b";
}
入出力はこんな感じになります。
入力 → 出力
fizz → [3]
buzz → [5]
fizz buzz → [9, 10]
buzz fizz → [5, 6]
buzz buzz → []
fizz buzz fizz fizzbuzz fizz buzz fizz → [9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21]
fizzbuzz fizzbuzz → []
このように最短解をちゃんと出力します。
結果的にラムダとUFCS,Rangeを多用した変態コードになりましたが、
別にD言語が変態というわけではなく、
D言語だと綺麗サッパリ書くことも出来ます。
ですから、この記事のソースコードをみて
D言語こわ...ちかよらんとこ...
とかそんなことは思わないでください。
D言語は良心的な紳士言語です。


<br>




