Upgrade to Pro — share decks privately, control downloads, hide ads and more …

List とは何か? / PHPerKaigi 2025

meihei
March 28, 2025

List とは何か? / PHPerKaigi 2025

meihei

March 28, 2025
Tweet

More Decks by meihei

Other Decks in Programming

Transcript

  1. © 2012-2025 BASE, Inc. 2 © 2012-2025 BASE, Inc. 2

    これだけ覚えて帰ってください
  2. © 2012-2025 BASE, Inc. 4 #phperkaigi #a 自己紹介 meihei |

    Yohei Ema BASE株式会社 BASE / Product Dev / Feature Dev 1 X: @app1e_s mixi2: @meihei2 GitHub: @meihei3
  3. © 2012-2025 BASE, Inc. 5 1 2 3 #phperkaigi #a

    仕様から見たリスト 静的解析ツールから見たリスト PHPの処理から見たリスト 3つの視点からお話しします
  4. © 2012-2025 BASE, Inc. 8 #phperkaigi #a RFC を読むと •

    PHP RFC: Add array_is_list(array $array): bool • リスト型を導入するわけではなく、配列が0から始まる連続した整数キーで あるかどうかを判定する関数を追加するだけ • (背景)PHPの配列は、整数キーと文字列キーの両方を持つことができ、 かつその順序が保証されている ◦ [0 => 0, 1 => 1, 2 => 2] と [0 => 0, 2 => 2, 1 => 1] は異なる • (使用例)エンコーダなどで効率的にリストを判定したい時に使う ◦ [0 => 0, 1 => 1, 2 => 2] ならば ‘[0, 1, 2]’ に ◦ [0 => 0, 2 => 2, 1 => 1] ならば ‘{“0”: 0, “2”: 2, “1”: 1}’ に
  5. © 2012-2025 BASE, Inc. 9 #phperkaigi #a 配列がリストである例 ['りんご', 'みかん',

    'バナナ'] キーをすべて省略した配列 [0 => 'りんご', 1 => 'みかん', 2 => 'バナナ'] キーが0から始まる連続した整数を明 示的にした配列 [false => 'りんご', true => 'みかん', '2' => 'バナナ'] キーが0から始まる連続した整数とみ なせる値の配列 [] 空配列 ['りんご', 1, 2, null, []] 値が何であっても、キーがリストであ る要件をみたす配列
  6. © 2012-2025 BASE, Inc. 10 #phperkaigi #a 配列がリストではない例 [1 =>

    'みかん', 2 => 'バナナ'] 連続した整数キーが0から始まらない 配列 ['りんご', 'みかん', 99 => 'かまぼこ'] 整数キー連続していない配列 ['a' => 'apple', 'b' => 'banana'] キーが整数ではない文字列の配列
  7. © 2012-2025 BASE, Inc. 11 #phperkaigi #a リストを返す配列関数(よく使うもの) array_keys, array_values,

    range 常にリストを返す array_chunk preserve_keys が false ならリスト を返す array_column index_key を指定しないならリストを 返す array_merge, array_map, array_slice, array_reverse 入力がリストならリストを返す
  8. © 2012-2025 BASE, Inc. 13 #phperkaigi #a 関数呼び出し時にリストか判定する $a =

    [ 0 => 'りんご', 3 => 'ぶどう', 1 => 'みかん', 2 => 'バナナ', ]; echo array_is_list($a); // FALSE unset($a[3]); echo array_is_list($a); // TRUE 1回目の呼び出し時は、キーが 0,3,1,2 の順になるからリストではな い。 2回目の呼び出し時は、キーが 0,1,2 の順になるからリストとなる。
  9. © 2012-2025 BASE, Inc. 14 © 2012-2025 BASE, Inc. 14

    静的解析ツールから見たリスト
  10. © 2012-2025 BASE, Inc. 16 #phperkaigi #a リスト型 • PHPの仕様上、リスト型は無い

    • 静的解析ツールではPHPDoc上で扱う擬似的な型としてリスト型がある • list<T> は array<int, T> のサブタイプとして定義される ◦ list<T> は、配列が array<int, T> であることに加えて、キーが0から始まる連続し た整数である配列 • 配列がリストであるかを、より厳格に型としてチェックできる
  11. © 2012-2025 BASE, Inc. 17 #phperkaigi #a 静的解析ツールによる型チェック /** *

    @param list<string> $x **/ function hello(array $x): void {...} $a = ['りんご', 'みかん', 99 => 'かまぼこ']; hello($a); // Error: Parameter #1 $x of function hello expects list<string>, array{0: 'りんご', 1: ' みかん', 99: 'かまぼこ'} given. PHPを実行する分にはエラーは発生し ない。 静的解析ツールを使うと、厳格に配列 とリストを区別するので、エラーが発 生する(PHPStan Level 5)。
  12. © 2012-2025 BASE, Inc. 18 #phperkaigi #a インライン @var タグもチェックする

    /** @var list<string> $a **/ $a = [ 0 => 'りんご', 1 => 'みかん', 99 => 'かまぼこ', ]; // Error: PHPDoc tag @var with type list<string> is not subtype of native type array{0: 'りんご', 1: 'みかん', 99: 'かまぼこ'}. コードの途中でローカル変数を定義す る場合でも、list<T> 型をチェックし てくれる(PHPStan 1.10, Level 2)。 以前は負の整数のキーに非対応だった が、つい先々週修正された。 phpstan/phpstan#12708 phpstan/phpstan-src#3870
  13. © 2012-2025 BASE, Inc. 19 © 2012-2025 BASE, Inc. 19

    PHPの処理から見たリスト
  14. © 2012-2025 BASE, Inc. 20 #phperkaigi #a Packed Array かつ空き領域

    (holes)を含んでいない配列 ならば 配列がリストである
  15. © 2012-2025 BASE, Inc. 21 #phperkaigi #a PHPの配列は2つある Hash Table

    • キーからハッシュ表を 使ってデータアクセス する • 連想配列を実装する Packed Array • 純粋な配列としてデータ アクセスする • メモリ効率が良く処理が高速
  16. © 2012-2025 BASE, Inc. 22 #phperkaigi #a Packed Array とリスト

    • Packed Array は、配列のキーが0から始まる連続した整数である時に 自動的に最適化された配列の内部構造 • 基本的にはリストと条件が同じである • しかし、unset などで空き領域(holes)が生まれることがある ◦ その場合も Packed Array では在り続けるので、Packed Array だけどリストではな い状態が生まれる。 • Packed Array から Hash Table へ自動的に変更されることはあるが、 Hash Table から Packed Array への自動的な変更されることはない ◦ リストだけど Packed Array ではない(Hash Table である)状態が存在する 参照: PHP8.2から見る、2つの配列 / PHP Conference Japan 2023 https://speakerdeck.com/meihei3/php-conference-japan-2023
  17. © 2012-2025 BASE, Inc. 23 #phperkaigi #a 配列がリストである多くの場合 // Packed

    Array $a = ['りんご', 'みかん', 'バナナ']; echo array_is_list($a); // TRUE 配列のキーが0から始まる連続した整 数であるので、自動的に Packed Array として最適化される。 この状態はメモリ効率が良くて、処理 も高速。
  18. © 2012-2025 BASE, Inc. 24 #phperkaigi #a Packed Array だがリストではない場合

    // Packed Array $a = ['りんご', 'みかん', 'バナナ']; unset($a[1]); echo array_is_list($a); // FALSE unset で要素を削除されただけでは、 Hash Table への自動的な変更は行わ れない。 ただ空き領域が生まれる。
  19. © 2012-2025 BASE, Inc. 25 #phperkaigi #a リストだが Packed Array

    ではない場合 // Hash Table $a = [ 0 => 'りんご', 3 => 'ぶどう', 1 => 'みかん', 2 => 'バナナ', ]; unset($a[3]); echo array_is_list($a); // TRUE 配列のキーが連番となっていないの で、Hash Table となる。 配列の再生成が行われない限り、 Packed Array から Hash Table へ自 動的に変更されることはない。
  20. © 2012-2025 BASE, Inc. 26 #phperkaigi #a まとめ • リストとは、キーが0から始まる連続した整数である配列

    • PHPの仕様では、リスト型は存在せず array_is_list が TRUE で ある配列のことをリストと言う • 静的解析ではリストを list<T> 型として表現する • 内部実装では Packed Array かつ空き領域(holes)を含んでいない配列な らば、配列がリストである • 多くの場合ではリストは高速でメモリ効率も良い • みんなリストを使おう!