TypeScriptでもPythonみたくrangeしたい!
公開日:
最終更新日:
はじめに
Pythonにあるrange
関数に似たものをTypeScriptで実装してみました。
Pythonのrange
関数とは
引数としてstart
とstop
とstep
の3つを受け取り、数列を作る関数です。
実装
export function* range({ start = 0, stop = Number.POSITIVE_INFINITY, step = 1 }) {
if (step === 0) throw new Error();
for (let index = start;; index += step) {
if (stop > 0 && !(index < stop)) break;
if (stop < 0 && !(index > stop)) break;
yield index;
}
}
テストコード as a 使用例
Denoを使っています。
import { range } from "./main.ts";
import { assertEquals } from "https://deno.land/[email protected]/testing/asserts.ts";
Deno.test("(10)", () => {
assertEquals(
[...range({ stop: 10 })],
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
);
});
Deno.test("(1, 11)", () => {
assertEquals(
[...range({ start: 1, stop: 11 })],
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
);
});
Deno.test("(0, 30, 5)", () => {
assertEquals(
[...range({ start: 0, stop: 30, step: 5 })],
[0, 5, 10, 15, 20, 25],
);
});
Deno.test("(0, 10, 3)", () => {
assertEquals(
[...range({ start: 0, stop: 10, step: 3 })],
[0, 3, 6, 9],
);
});
Deno.test("(0, -10, -1)", () => {
assertEquals(
[...range({ start: 0, stop: -10, step: -1 })],
[0, -1, -2, -3, -4, -5, -6, -7, -8, -9],
);
});
Deno.test("(0)", () => {
assertEquals(
[...range({ stop: 0 })],
[],
);
});
Deno.test("(1, 0)", () => {
assertEquals(
[...range({ start: 1, stop: 0 })],
[],
);
});
性能
[...range({ start: 0, stop: 1000, step: -3 })]
を1回だけ実行するのにかかる時間は、手元のパソコンでは7.64μsでした。
ただしこれはイテレータからスプレッド構文ですべての要素を取り出して配列にしているので、すべての要素には用がない場面(for-of
ループの途中でbreak
するときなど)ではその分の計算コストを省けます。極端な話をすると、イテレータの作成だけしてスプレッド構文を使った配列化をしなかったら、1回の実行では52nsしかかかりません。
ただ、すべての要素に用がある場面では下のようなnew Array()
を使ったものの方が高速です。
export const range = ({
start = 0,
stop = Number.POSITIVE_INFINITY,
step = 1,
}) => {
if (step === 0) throw new Error();
const length = Math.ceil((stop - start) / step);
if (length <= 0) return [];
return new Array(length).fill(null).map((_, i) => {
return start + i * step;
});
};
おわりに
TypeScriptなのに型定義ねーじゃーん!(なくても推論されるため)