あと味

たくさん情報を食べて、たくさん発信すると、あとになって味わい深い。

Lisp脳なFizzBuzzをJavaScriptで書いてみた

最近、JavaScriptに惹かれる理由は、関数型言語の特徴を持つところに原因があるような気がしています。

Lispを得意としている人は、Lisp脳なる考え方があるとのことです。

興味の源泉に出会うために、Lisp脳を覗いてみようと思い、を読みながら、JavaScriptでLisp脳なFizzBuzz*1を書いてみました。

お手本

(for-each print
  (map
    (lambda (x) (cond ((= (modulo x 15) 0) "FizzBuzz")
                      ((= (modulo x 5) 0) "Buzz")
                      ((= (modulo x 3) 0) "Fizz")
                      (else x)))
    (iota 100 1)))

これを読むだけでは、正直何をやってるかわかりません。次に、解説を読みながら書いてみます。

まずはリストを作る

「とりあえず1から100までのリストを作れば良さそうだ」

Lisp脳な人は、リストを中心にプログラムを考えるみたいです。まず、この発想が私にはありません。

これをJavaScriptで書いてみます。JavaScriptにiotaなんて関数はないので、無名関数で。*2

(function(max, start) {
  var list = [];
  for (var i = start; i <= max; i++) {
    list.push(i);
  }
  return list;
})(100, 1)

map関数でリストを加工する

「mapを使ってこのリストを加工して、別のリストを返せばいいじゃん」

map関数もJavaScriptにないので、無名関数を使います。

「その要素を置き換えておけば、出力は後からどうにでもなるじゃないか」

map関数の機能を持った無名関数を使って、さっき作ったリストを加工します。具体的には、作ったリストの3の倍数と5の倍数と3と5の両方の倍数の箇所を、それぞれ、Fizz、Buzz、FizzBuzzに置き換えます。

(function(func, list) {
  var clone = [];
  for (var i = 0; i < list.length; i++) {
    clone[i] = func(list[i]);
  }
  return clone;
})(function(x) {
  if (x % 15 === 0) {
    x = 'FizzBuzz';
  }
  else if (x % 5 === 0) {
    x = 'Buzz';
  }
  else if (x % 3 === 0) {
    x = 'Fizz';
  }
  return x;
}, (function(max, start) {
  var list = [];
  for (var i = start; i <= max; i++) {
    list.push(i);
  }
  return list;
})(100, 1));

出力を考える

「プリントせよ? 対話型インタプリタで実行すれば結果のリストはインタプリタが印字してくれるじゃないか」

「まあ結果のリストをprintしてみても良いか」

printの代わりにデバッグコンソールを使っていることを前提で、console.log() で出力してみます。

console.log((function(func, list) {
  var clone = [];
  for (var i = 0; i < list.length; i++) {
    clone[i] = func(list[i]);
  }
  return clone;
})(function(x) {
  if (x % 15 === 0) {
    x = 'FizzBuzz';
  }
  else if (x % 5 === 0) {
    x = 'Buzz';
  }
  else if (x % 3 === 0) {
    x = 'Fizz';
  }
  return x;
}, (function(max, start) {
  var list = [];
  for (var i = start; i <= max; i++) {
    list.push(i);
  }
  return list;
})(100, 1)));

要素ごとに改行して出力する

JavaScriptは、for-eachが使えない環境も多いっぽいので、ここも無名関数で。

(function(proc, list) {
  for (var i = 0; i < list.length; i++) {
    proc(list[i]);
  }
})(function(x) {
  console.log(x);
}, (function(func, list) {
  var clone = [];
  for (var i = 0; i < list.length; i++) {
    clone[i] = func(list[i]);
  }
  return clone;
})(function(x) {
  if (x % 15 === 0) {
    x = 'FizzBuzz';
  }
  else if (x % 5 === 0) {
    x = 'Buzz';
  }
  else if (x % 3 === 0) {
    x = 'Fizz';
  }
  return x;
}, (function(max, start) {
  var list = [];
  for (var i = start; i <= max; i++) {
    list.push(i);
  }
  return list;
})(100, 1)));

うーむ、無駄にややこしい。合ってるかどうかもわかんないし。

ただ、こうやってひとつひとつの関数を次の関数に渡していくと、関数がこの世界を支配している感覚を垣間見れたような気がします。さらにこの発想が当たり前になれば、簡潔に書けるLispの使い勝手は、なかなか良いのかもしれないという感覚を持つことができました。

ということで、今後、関数型言語に入門してみる次第です。

*1:3の倍数でFizz, 5の倍数でBuzz, 3と5の両方の倍数でFizzBuzzを出力するプログラム

*2:変数や関数にいちいち名前付けるのが面倒なので、無名関数を多用する傾向にあります。改めたほうがいいのかしら。。。