私はphpで再帰を少し遊んでいます。残念ながら、phpはアイデアをスマートにコピーし、デフォルトでオブジェクトを参照で渡すことはそれを容易にしません。反復番号Xによって行われた変更は、反復ex X-2で表示されるため、問題が発生します。
例えば:
/**
* Silly function to find last element in array
* @param ArrayObject $input
*/
function process(ArrayObject $input) {
if ($input->count() == 1) {
return $input->getIterator()->current();
}
$ar = $input->getArrayCopy();
array_shift($ar);
$input->exchangeArray($ar);
return process($input);
}
$in = new ArrayObject(range('a', 'd'));
echo 'Before ' . PHP_EOL;
var_dump($in);
echo PHP_EOL . 'Process - last element is: ' . process($in) . PHP_EOL;
echo 'After ' . PHP_EOL;
var_dump($in);
出力は
Before
object(ArrayObject)#1 (1) {
["storage":"ArrayObject":private]=>
array(4) {
[0]=>
string(1) "a"
[1]=>
string(1) "b"
[2]=>
string(1) "c"
[3]=>
string(1) "d"
}
}
Process - last element is: d
After
object(ArrayObject)#1 (1) {
["storage":"ArrayObject":private]=>
array(1) {
[0]=>
string(1) "d"
}
}
再帰が元の$in変数も変更されているのを見ると、私が期待しているのは、すべての新しい反復が値のコピーで動作することです。この例では必要ありませんが、再帰関数がより複雑な場合はどうでしょうか。
簡単な解決策-クローン
function process(ArrayObject $input) {
$input = clone ($input);
if ($input->count() == 1) {
return $input->getIterator()->current();
}
$ar = $input->getArrayCopy();
array_shift($ar);
$input->exchangeArray($ar);
return process($input);
}
わかりました、動作しますが、入力が非常に複雑なマルチオブジェクトのネストされた繰り返し構造である場合はどうなりますか?さて、ディープコピーの代わりに値をシリアル化およびアンシリアル化できます
function process(ArrayObject $input) {
$input = unserialize(serialize($input));
if ($input->count() == 1) {
return $input->getIterator()->current();
}
$ar = $input->getArrayCopy();
array_shift($ar);
$input->exchangeArray($ar);
return process($input);
}
完璧です-動作しますが、すべての反復でアン/シリアル化するのは時間とCPUの無駄です(特に巨大な$ inputの場合)。
それを行う他の方法はありますか?標準的な使用法でphpを使用するために「少しハックする」必要があることを心配しないでください。
//Tigrangの提案で例を編集
あなたの提案をTigrangすることは非常に興味深いものでしたが、
function process(ArrayObject $input) {
$input = new ArrayObject(($input));
$input->offsetSet(null, 'f');
}
$in = new ArrayObject(range('a', 'd'));
echo 'Before ' . PHP_EOL;
var_dump($in);
process($in);
echo 'After ' . PHP_EOL;
var_dump($in);
After
object(ArrayObject)#1 (1) {
["storage":"ArrayObject":private]=>
array(5) {
[0]=>
string(1) "a"
[1]=>
string(1) "b"
[2]=>
string(1) "c"
[3]=>
string(1) "d"
[4]=>
string(1) "f"
}
}
それでも外部変数への参照を保持します
//編集2
class Engine {
public $power = 999;
}
class Car {
public $name = '';
public $engine = '';
public function __construct($name, $power) {
$this->name = $name;
$this->engine = new Engine();
$this->engine->power = $power;
}
}
function process(ArrayObject $input) {
$input = new ArrayObject(($input->getArrayCopy()));
$ford = $input[0];
$ford->name = 'Audi';
$ford->engine->power = 1500;
}
$ar = array(new Car('Ford', 130));
$in = new ArrayObject($ar);
echo 'Before ' . PHP_EOL;
var_dump($in);
process($in);
echo 'After ' . PHP_EOL;
var_dump($in);