diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ef6934a..17d265ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ # 未リリース分 - `Date:year`系の関数に0を渡すと現在時刻になる問題を修正 - シンタックスエラーなどの位置情報を修正 +- `arr.reduce`が空配列に対して初期値なしで呼び出された時、正式にエラーを出すよう # 0.18.0 - `Core:abort`でプログラムを緊急停止できるように diff --git a/docs/primitive-props.md b/docs/primitive-props.md index 609cc6ee..1356e1c3 100644 --- a/docs/primitive-props.md +++ b/docs/primitive-props.md @@ -140,11 +140,13 @@ _i_ 番目の文字が存在しない場合は null が返されます。 配列の要素のうち _func_ が true を返すようなもののみを抜き出して返します。 順序は維持されます。 -### @(_v_: arr).reduce(_func_: @(_acm_: value, _item_: value, _index_: num) { value }, _initial_: value): value +### @(_v_: arr).reduce(_func_: Callback, _initial_: value): value +`Callback`: @(_acm_: value, _item_: value, _index_: num): value 配列の各要素に対し _func_ を順番に呼び出します。 各呼び出しでは、前回の結果が第1引数 _acm_ として渡されます。 _initial_ が指定された場合は初回呼び出しの引数が(_initial_, _v_\[0], 0)、 指定されなかった場合は(_v_\[0], _v_\[1], 1)となります。 +配列が空配列であり、かつ _initial_ が指定されていない場合はエラーになります。従って基本的には _initial_ を指定しておくことが推奨されています。 ### @(_v_: arr).find(_func_: @(_item_: value, _index_: num) { bool }): value 配列から _func_ が true を返すような要素を探し、その値を返します。 diff --git a/src/interpreter/primitive-props.ts b/src/interpreter/primitive-props.ts index c154c45a..f158546c 100644 --- a/src/interpreter/primitive-props.ts +++ b/src/interpreter/primitive-props.ts @@ -183,6 +183,7 @@ const PRIMITIVE_PROPS: { reduce: (target: VArr): VFn => FN_NATIVE(async ([fn, initialValue], opts) => { assertFunction(fn); const withInitialValue = initialValue != null; + if (!withInitialValue && (target.value.length === 0)) throw new AiScriptRuntimeError('Reduce of empty array without initial value'); let accumulator = withInitialValue ? initialValue : target.value[0]!; for (let i = withInitialValue ? 0 : 1; i < target.value.length; i++) { const item = target.value[i]!; diff --git a/test/index.ts b/test/index.ts index fed26c01..bfb716a4 100644 --- a/test/index.ts +++ b/test/index.ts @@ -4,6 +4,7 @@ */ import * as assert from 'assert'; +import { expect, test } from '@jest/globals'; import { Parser, Interpreter, utils, errors, Ast } from '../src'; import { NUM, STR, NULL, ARR, OBJ, BOOL, TRUE, FALSE, ERROR ,FN_NATIVE } from '../src/interpreter/value'; let { AiScriptRuntimeError, AiScriptIndexOutOfRangeError } = errors; @@ -2753,6 +2754,13 @@ describe('primitive props', () => { eq(res, NUM(20)); }); + test.concurrent('reduce of empty array without initial value', async () => { + await expect(exe(` + let arr = [1, 2, 3, 4] + <: [].reduce(@(){}) + `)).rejects.toThrow('Reduce of empty array without initial value'); + }); + test.concurrent('find', async () => { const res = await exe(` let arr = ["abc", "def", "ghi"]