2

私は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);
4

2 に答える 2

3

AFAIK、これはPHPの制限です。ご指摘のとおり、これはPHP5xの2つの機能が補完的ではないために発生します。

  • オブジェクトは常に参照として渡されます
  • clone()は浅いコピーのみを行い、言語に固有の深いコピーメカニズムはありません

unserialize(serialize($obj)現在利用可能な唯一の(非常に非効率的な)回避策です。神々が、大騒ぎの(そして遅れた)バージョン6でユニコードを実装する方法について頭を悩ませるのではなく、今後のバージョンでこれらの問題のいくつかを整理することを期待しましょう!

于 2012-08-22T22:26:38.107 に答える
0

なぜだめですか$input = new ArrayObject($input);

私が知る限り、それは深くネストされたコピーであり、シリアル化よりも高速です:http: //viper-7.com/kHLCCP

于 2012-08-22T22:59:26.333 に答える