二律背反の要求を実現したい。
WebAPIの無駄な呼び出し回数を少しでも減らしたい。回数を減らし、プログラム応答速度を上げたい。
ただ、コードの書き換えは極小にしたい。
この目的では、Generatorを使うのが、鉄板だと思うんですよね。
WEBを呼び出すコードのサンプル。
次のようなAPIを叩くコードがあってさ。
<?php function my_list_items(){ foreach( $api->call('list_item') as $id){ $list[]=$api->call('get_item', $id); } return $list; } $items = $my_list_items(); $item = $items[0];
要素を全部使わないのに、毎回全部取ってしまうんですよね。もったいないですよね。API呼び出し回数に時間あたり制限とかあったり、高頻度呼び出しが攻撃判定されちゃったりして辛いんですよね。
Generator ( yield ) で解決する。
yield
を使えば、だいたい解決する。
<?php function my_list_items(){ foreach( $api->call('list_item') as $id){ $item = $api->call('get_item', $id); yield $item; //<=yield を使って書き換える。 } } // $items = $my_list_items(); $item = $items[0];//<= ここがエラーになる。
yield
を使えばいいの。だいたい解決する。だけど、yield
の結果はGenerator
である。Generator
だとArrayとはアクセス方法が異なる。foreach互換性(ある程度)がある。ただ、Arrayとの互換性が全くない。コードの修正箇所が増えてしまう。修正箇所を探すだけでも困難だ。
<?php $item = $items[0];//<= 配列のアクセスが使えない $item = $items->corrent();//<= メソッドを使う必要がある。
とくに、current()
のようなメソッドを使って先頭を取るのが、ソースコードの可読性を著しく損なうというか。逆に型がわかりにくい。
CachingIterarorを使う。
php の場合。CachingIteraror
を使うのが鉄板だが、CachingIterarorには問題がある。
<?php function my_list_items(){ return new \CachingIteraror((function(){ foreach( $api->call('list_item') as $id){ $item = $api->call('get_item', $id); yield $item; } })()); } $items = $my_list_items(); $item = $items[0];//<= BadMethodCallException になる。
CachingIteraror
だと、一見すると解決しそうなんだけど、キャッシュするまでは、BadMethodCallException
になる。( php 8.2 でも)
一度キャッシュする必要がある。
<?php $items = $my_list_items(); $item = $items[0];//<= BadMethodCallException になる。
キャッシュするまでアクセス不能で、キャッシュ後は気軽にアクセスできるって、コードの見通しが良くない。配列でアクセスしたいのか、手続きに他の要因が関連してるように見えてしまい、コードのメンテナンス性能を喪失してしまう。
<?php $item = $items[0];//<= BadMethodCallException になる。 $items->corrent();//<=キャッシュする。 $item = $items[0];//<= アクセス可能になる。
こんな問題があってGeneratorは避けてたんだけど、APIコールが遅いのでなにか手を考える必要があって。やっぱりGeneratorしか無い。となるが、Generatorを見せないで使いたい。
そのうち、公式で解決しそうなんだけど。解決しそうなんだけど、って思ってたのだが、公式では解決しそうに無い。今後の更新も型システムについてばかり。*1
中間クラスがほしい。
ArrayAccessをサポートしつつCachingもサポートしたそんなクラスがほしい。
<?php function my_list_items(){ return new \MySomeClass((function(){ foreach( $api->call('list_item') as $id){ $item = $api->call('get_item', $id); yield $item; } })()); } $items = $my_list_items(); $item = $items[0];//<= コードの書き換えがない。
とりあえず作った。
yield
を使うときの、このような問題を手軽に解決したくて、コードを書きました。
もっといい解決方法はないのだろうか。