🌊
JavaScript で Python の range みたいなやつ
主題
JavaScript には Python の range
みたいなやつが標準で存在しない。Python で以下のように書けるコードは
Python
print(list(map(lambda x: x * 2, range(5))))
# [0, 2, 4, 6, 8]
JavaScript で愚直に書くと、以下のようになってしまう。
JavaScript
const start = 0;
const stop = 5;
const step = 1;
const length = Math.floor((stop - start) / step);
const a = new Array(length);
for (let i = 0; i < length; i++) {
a[i] = start + step * i;
}
console.log(a.map(x => x * 2));
// [0, 2, 4, 6, 8]
私が思うに、このコードには 2 つの問題点がある。
- 空の配列を作って、
for
文で値を計算して代入して、と長いコードを書いて反復処理可能なオブジェクトを作成しなければならない。 - Python の
range
に相当する配列をmap
する前に作成してしまっている。
「JavaSciprt で Python の range みたいなものを使うにはどうすれば良いか」を Google で検索すると、
[...Array(5).keys()]
などとする方法や
Array.from(Array(5).keys)
などとする方法が出てくるが、最終成果物に関係のない Array(5)
を作成する必要があるし、任意の start
, stop
, step
に使用することができず、2の問題点も解決できていないので、邪道に思える。
個人的なおすすめとしては、生成関数 (generator function) で自作する方法である。生成関数が返すオブジェクトは Generator だ。継承される Iterator
の Iterator.prototype.forEach
や Iterator.prototype.map
や Iterator.prototype.toArray
は Chrome とかでしかまだ使えないので、Array.from
を使って Array
に変換する必要が多いだろう。その意味では2の問題点は解消できていないかもしれない。そのうち Array.from
を使わなくても良くなると思う。MDN Web Docs の Iteration protocols を見ると良い。
Python の range と大体同じなやつ
function * range(start, stop, step = 1) {
if (start === undefined) {
throw new Error("Need at least one argument.");
}
if (stop === undefined) {
stop = start;
start = 0;
}
if (step > 0) {
for (let n = start; n < stop; n += step) {
yield n;
}
} else if (step < 0) {
for (let n = start; n > stop; n += step) {
yield n;
}
} else {
throw new Error("step cannot be zero.");
}
}
console.log(Array.from(range(5))); // [ 0, 1, 2, 3, 4 ]
console.log(Array.from(range(-2, 2))); // [ -2, -1, 0, 1 ]
console.log(Array.from(range(0, 10, 2))); // [ 0, 2, 4, 6, 8 ]
console.log(Array.from(range(10, 0, -2))); // [ 10, 8, 6, 4, 2 ]
おまけ
私は Julia に慣れてしまっているので、Python の range
よりも Julia の StepRange
の方が使いやすい。range(n)
で 1:n
ということにして実装すると
Julia の StepRange と大体同じなやつ
function * range(start, step, stop) {
if (start === undefined && step === undefined && stop === undefined) {
throw new Error("Need at at least one argument.");
}
if (step === undefined && stop === undefined) {
stop = start;
start = 1;
step = 1;
}
if (stop === undefined) {
stop = step;
step = 1;
}
if (step > 0) {
for (let n = start; n <= stop; n += step) {
yield n;
}
} else if (step < 0) {
for (let n = start; n >= stop; n += step) {
yield n;
}
} else {
throw new Error("step cannot be zero.");
}
}
となる。
Discussion