feat: chunk と windowed を追加する - #79
Conversation
e4612d0 to
6096f82
Compare
phpdoc の型は実行時には強制されないため、positive-int のような整数レンジ型を宣言しつつ実行時の防御($size <= 0 での例外)も残したい。既定では両者が共存できず「always false」と報告されるため、phpdoc 由来の型を確定として扱う挙動を無効にして型宣言と実行時の防御を両立させる。 native type 由来の常真・常偽検出は残るため、リテラル配列由来の identical.alwaysFalse 等は引き続き検出される。設定変更で無効になる ignore 2 箇所(method.alreadyNarrowedType)を併せて削除する。 Refs #70, #71
$size / $step は 1 以上しか意味を持たないため positive-int を宣言した。実行時の例外チェックは phpdoc の型が実行時に強制されないため残している(特に windowed の $step <= 0 は無限ループになる)。 戻り値は空のチャンク・空の窓を作らないので list 側を non-empty-list に絞った。assoc 側は non-empty-array<never, never> が解決できず空配列リテラルを渡す呼び出しを壊すため array のままにし、理由を phpdoc に残した。その回帰を捕まえるテストとして、空配列リテラルを直接渡す呼び出しを各モードで追加した。 異常系テストは positive-int の宣言を int 経由ですり抜けるヘルパーを介して維持した。 Refs #70, #71
|
Claude finished @Javakky's task in 4m 46s —— View job コードレビュー
総評ロジックにバグは見つかりませんでした。 条件型の健全性も 4 パターン( 指摘は blocker なし、設計判断の相談 1 件とテスト/PR本文の nit のみです。 1.
|
| ワークフロー | 結果 |
|---|---|
Check (62b2005) |
✅ success |
.claude/rules/20_quality.md の 3 コマンドは CI 側で緑になっているため、PR 本文の実行結果(125 tests / PHPStan level 10 no errors / CS Fixer 0 fixed)は信頼できると判断しました。上記コマンドを自分で回すには --allowedTools に Bash(composer install) / Bash(vendor/bin/*) の許可が必要です。
コード変更は行っていません(レビュー依頼のため)。
· ブランチ: javakky/chunk-windowed
概要(What / Why)
固定サイズで分割する
chunkと、スライディング窓を作るwindowedを追加します。変更点
src/chunk.php—array_chunk相当 + mode 対応。size <= 0はInvalidArgumentExceptionsrc/windowed.php— 開始位置を step 刻みで進める。partialで末尾の欠けた窓を含めるか選ぶtests/ChunkTest.php(11 ケース) /tests/WindowedTest.php(16 ケース)composer.jsonのautoload.filesに 2 件追加動作確認
windowed([1,2,3,4,5], 3)→[[1,2,3], [2,3,4], [3,4,5]]windowed([1,2,3,4,5], 3, 2, true)→[[1,2,3], [3,4,5], [5]]chunk([1,2,3,4,5], 2)→[[1,2], [3,4], [5]]vendor/bin/php-cs-fixer fix --dry-run --diff/vendor/bin/phpstan analyse -c phpstan.neon/vendor/bin/phpunit testsOK (125 tests, 137 assertions)/ PHPStan level 10[OK] No errors/ CS FixerFixed 0 of 33 files補足(任意)
windowedの窓生成規則Kotlin の
windowed(size, step, partialWindows)に合わせました。開始位置は 0, step, 2*step, ... と進み、partial = trueのときは開始位置が件数未満であれば残りが size 未満でも窓を作ります (空の窓は作りません)。直感に反しやすいので phpdoc に例を置き、テストでも固定しています。
array_sliceの落とし穴array_slice()は$preserve_keys = falseでも文字列キーを保持します (再付番されるのは数値キーだけ)。そのためwindowedはarray_slice(..., true)で切り出してからMODE_LISTのときだけarray_values()する実装にしています。chunkが使うarray_chunk()にはこの問題がないので委譲のままです。なお既存の
src/slice.phpはこの対処がなく、slice(['a'=>1,'b'=>2], 0, 2, Mode::MODE_LIST)が['a'=>1,'b'=>2]を返して宣言しているlist<V>に反します。既存関数の変更はこの PR のスコープ外なので触っていませんが、別 issue に切り出す価値があります。その他
$size は 1 以上である必要があります。) が先例になります。文体を揃えたい場合は指摘してくださいchunk_by(隣接要素をキーの変化で切る関数) は feat: chunk_by を追加 #75 で別途追加しています。用途が異なります@phpstan-paramの条件型は付けていません (「assoc 入力 +MODE_LIST」が型エラーになるため。slice.php/map.php/unique.phpと同じ形)Closes #70
Closes #71
🤖 Generated with Claude Code
追記: 整数レンジ型を入れました (2 コミット追加)
オーナーの方針 (
positive-int/non-negative-intを積極的に使う。特に$sizeを比較するような場合) を反映しました。ci: PHPStan の treatPhpDocTypesAsCertain を無効にする@param positive-int $sizeと実行時のif ($size <= 0) throwは level 10 の既定では共存できません。phpdoc の型は実行時に強制されないので、ライブラリとしては型宣言と実行時の防御を両立させたいところです。特に
windowedの$step <= 0を消すと$offsetが進まず無限ループします ($size <= 0の方は空の窓が並ぶ静かな失敗)。treatPhpDocTypesAsCertain: falseを入れた副作用は実測しました。既存の@phpstan-ignore2 箇所が無効になるだけで (HeadOptionTest/LastOptionTestのmethod.alreadyNarrowedType)、同じコミットで削除しています。native type 由来の常真/常偽検出は残るので、src/reduce.phpとtests/AnyTest.phpの ignore は有効なまま残しました。設定変更の前後で PHPStan のエラーは 0 → 0 です。refactor: chunk と windowed に整数レンジ型を入れるchunk:@param positive-int $sizewindowed:@param positive-int $size/@param positive-int $stepnon-empty-list<V>に絞る (空のチャンク・窓は作らないため)non-emptyを付けていません —non-empty-array<never, never>が unresolvable になり、chunk([], 2)のような空配列リテラルの呼び出しを壊すためです (実測で確認し、回帰テストを追加しました)windowedに\assert($window !== []);を 1 行入れています。array_slice()の戻り値はarray<K, V>なのでnon-empty-list<V>に絞るとreturn.typeで落ちるためで、ループ不変条件 ($offset < $countかつ$size >= 1) の表明も兼ねています。到達しないif分岐を作るより素直だと判断しましたが、リポジトリの好みに合わなければ言ってください。異常系テスト (
$size = 0/-1、$step = 0/-1) はpositive-int宣言後もそのままでは通らないので、intを一段挟んで widening する private ヘルパー経由にしました。テストは 1 件も削っていません (理由はヘルパーの docblock に記載)。追加コミットのフッターは
Refs #70, #71にしてあります (型注釈に対応する issue が無いため、Closesは既存 2 コミットのぶんだけです)。