あと味

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

Pythonのrange関数をJavaScriptで再帰を使って実装してみた

Pythonにrange関数という数列を作る関数があるんですが、JavaScriptにもrange関数みたいなのがあると便利かもしれないと思って実装してみました。

関数型言語慣れするために再帰で書いたので、あまり長い数列は作れません。

Pythonのfor文

Pythonでは1から10までの数字をプリントする処理はこんな感じで書きます。

for i in range(1,11):
  print i

range関数実装後のJavaScriptのコード

JavaScriptでもこうやって書くと同じような結果になります。*1

range(1,11).forEach(function(i) {
  console.log(i);
});

range関数のコード

function range(start, end, step) {
  return (
    (start === undefined) ?
      new Error('range expected at least 1 arguments, got 0') :
    (typeof start !== 'number') ?
      new Error('range() integer start argument expected, got ' + typeof start) :
    (end !== undefined && typeof end !== 'number') ?
      new Error('range() integer end argument expected, got ' + typeof end) :
    (step !== undefined && typeof step !== 'number') ?
      new Error('range() integer step argument expected, got ' + typeof step) :
    (step === 0) ?
      new Error('range() step argument must not be zero') :
    (arguments[3]) ?
      new Error('range expected at most 3 arguments, got ' + arguments.length) :

    (function(start, end, step, list) {
      return (
        (step > 0 && start < end || step < 0 && start > end) ?
          arguments.callee(start+step, end, step, list.concat(start)) :
        list
      );
    })(end === undefined ? 0 : start, end === undefined ? start : end, step === undefined ? 1 : step ,[])
  );
}

引数は、start(開始値)、end(終了値)、skip(増減値)が指定できて、startのみが必須の引数です。各引数にはマイナスの値も設定できます。

追記

id:ats2019さんにもコメントいただいきましたが、Pythonのrange関数のように、range(5)と第一引数しか指定しない場合は[0,1,2,3,4]が返りますし、range(10, -10, -2)のように、負の値も指定できる実装にしてあります。

Pythonに興味があれば、ぜひ、「みんなのPython 改訂版」読みましょう!(お礼)

そう言えば、プログラミングなんてほとんどわからない時期に読んで、レビュー書いてました。

range関数に設定した制約と、その他のぼやき

このrange関数を作るにあたって、最初に制約を設けました。

function range(start, end, skip) {
  return /* 残りのコードはすべてreturn内に書く */;
}

というコードを書いて、残りのすべてのコードはreturn以下に配置するという制約です。手続きを書かないことで、関数型言語のプログラミング思考が少しでもわかったらいいなと思いまして。

この制約のおかげで、良い頭の体操になった気がします。(一部、無名関数でラップしてるのが反則な気もしないではないですが)

不正な値が引数に代入されると、無限ループに陥るリスクが高いので、いつも適当に流してるエラー処理も吟味できました。エラーメッセージはPythonインタプリタが吐くメッセージを拝借しました。

JavaScriptはreturnのせいで、無駄にインデントが長くなるのがうざいですね。returnの返り値全体を括弧で囲っているのは、単にインデント幅を少しでも短くするためです。

引数を三項演算子で初期化している箇所は、1行がすごく長くなって見づらいですが、個人的には引数内に改行がある方が見づらいので、そのままにしてあります。制約がなければ、冒頭に変数の初期化処理を書くだけで済みますが。

制約のために、全体を通してすごく読みづらくなったので、いろんなコーディング規約を調べてみるキッカケにもなりました。

プログラミングにおいて、こういうマゾプレイを強要するととても良い経験になる気がするので、これからも無理がない程度にどんどん頭をイジめていきたい次第です。

*1:JavaScript1.6以降のみ対応