33

이 질문에는 이미 답변이 있습니다.

다음은 간단한 루프입니다.

$list = array("A", "B", "C","D");
foreach ($list as $var) {
    print(current($list));
}

출력 (데모)

 BBBB   // Output for 5.2.4 - 5.5.0alpha4
 BCD    // Output for 4.4.1
 AAAA   // Output for 4.3.0 - 4.4.0, 4.4.2 - 5.2.3

의문 :

  • 누군가가 무슨 일이 일어나는지 설명해 주시겠습니까?
  • 왜 내가 ABCD를 얻지 못하는거야?
  • 어레이의 사본이에 의해 만들어진 경우에도foreach나는해야한다.AAAA하지만 현재 상황에서는 그렇지 않다.PHP안정 버전

참고 * 나는 내가 쉽게 사용할 수 있다는 것을 알고있다.print $var하지만 from PHP DOC

current - 배열의 현재 요소를 반환합니다.   그만큼흐름()함수는 단순히 내부 포인터에 의해 가리켜지고있는 배열 요소의 값을 반환합니다. 어떤 식 으로든 포인터를 움직이지는 않습니다. 내부 포인터가 요소 목록의 끝을 초과하여 가리 키거나 배열이 비어 있으면 current ()는 FALSE를 반환합니다.

업데이트 1 - 새로운 관측

감사합니다.다니엘 피거 로아: 포장만으로current함수에서 다른 결과를 얻는다.

foreach ( $list as $var ) {
    print(item($list));
}

function item($list) {
    return current($list);
}

출력 (데모)

 BCDA   // What the hell 

의문 :

  • 왜 "BBBB"를 얻지 못합니까?
  • 함수의 래핑 전류는 함수에 어떤 영향을 줍니까?foreach출력?
  • 여분의 "A"는 어디에서 왔습니까?

업데이트 2

$list = array("A","B","C","D");
item2($list);
function item2($list) {
    foreach ( $list as $var ) {
        print(current($list));
    }
}

출력 (데모보기)

AAAA // No longer BBBB when using a function

의문 :

  • 함수에서 다른 루프를 실행하고 함수 밖에서 실행하는 것은 무엇입니까?AAAA외부와BBBB대부분의 PHP 버전의 함수에서


8 답변


18

B로 시작하는 이유는 무엇입니까?

5.2 이후foreach(신뢰할 수있게) 배열 포인터를 전진시킨다.전에루프 본문이 시작됩니다. 참조 항목FE_RESETopcode.

$list = array("A", "B", "C","D");
foreach ($list as $var) {
    break;
}
var_dump(current($list));

산출:

B

이것은 무엇을 할 수 있습니다.ZEND_OP_DATA의사 연산 코드가 작동합니다 (실제로 문서화되지 않았습니다).

왜 그래?current()같은 가치를 계속 주시겠습니까?

루프가 시작되기 전에,foreach루핑하는 배열에 대한 내부 참조를 만듭니다. 루프 내에서 배열 변수가 수정되거나 참조로 전달 될 때마다 내부 참조는 배열 구조의 복사본을 만들어 변수에서 분리됩니다 (요소는 제외). 이 복사 된 값은 배열 포인터 (루프 초기화에 의해 이전에 수정 된)를 유지합니다.

이 동작은 또한 더 파괴적인unset()조작:

$list = array('A', 'B', 'C', 'D');
foreach ($list as $key => $val) {
  echo $val;
  unset($list[1], $list[2], $list[3]);
}
echo "\n", print_r($list, true), "\n";

산출:

ABCD
Array
(
    [0] => A
)

루프 변수를 함수에 전달

이것은 또 다른 흥미로운 시나리오입니다.

$list = array('A', 'B', 'C', 'D');
function itm($arr) 
{
    return current($arr);
}

foreach ($list as $item) {
    print itm($list);
}
var_dump(current($list));

산출:

BCDA
bool(false)

이번에는 배열이 값에 의해 전달되므로 배열 구조가 요소가 아닌 함수로 복사됩니다.$arr매개 변수. 앞의 예와 달리 루프의 내부 참조와$list왜냐하면 복사가 기능 영역에서 일어나기 때문입니다.

마지막은 어떨까요?"A"?

이것은 지금까지 가장 신비로운 행동입니다.foreach이러한 상황에서만 목격 될 수 있습니다. 마지막 루프 반복에서 배열 포인터는 다음과 같습니다.겉으로는첫 번째 항목으로 되감기. 겉으로보기에는 루프의 끝 부분에서 요소의 끝 부분을 가리키고 있기 때문입니다 (출력의 마지막 줄에서 볼 수 있듯이).

이 문제는SWITCH_FREE의 마지막에 실행되는 opcodeforeach.

그렇다면 왜foreach함수가 다르다면?

다음 코드를 살펴보십시오.

function item2($arr) 
{
    foreach ($arr as $var) {
        print(current($arr));
    }
    var_dump(current($arr));
}
$list = array("A","B","C","D");
item2($list);

산출:

AAAA
string(1) "A"

이 경우 내부 참조는foreach배열의 복사본으로 초기화됩니다 (refcount> 1이기 때문에).$arr상징.

더 심해질 수 있습니까?

당연하지! 참조를 사용하거나 여러 번 중첩 할 때 심지어 더 까다로운 결과를 얻을 수 있습니다.foreach같은 변수에 루프.

그렇다면 어떻게 일관된 결과를 얻을 수 있습니까?

용도Iterators또는 배열 변수를 참조 할 때 일관성있는 값을 얻는 것에 의존하지 마십시오.foreach조작.


  • + 좋지만 어떻게 설명할까요?stackoverflow.com/a/14849560/1226894 - Baba
  • 내가 이해하지 못하는 이유는rewind온 ? 그것은 함수 rewinds 배열을 의미합니까 ??? 또는 return 문이 배열에 영향을 미침 - Baba
  • 함수에서 루프를 돌리는 것이 3 가지 다른 결과를 제공한다는 것을 알고 있습니까?3v4l.org/1aUpd반복자는 같은 결과를 준다.3v4l.org/ViCNn - Baba
  • @ 바바 그래, 나는 그걸 또한 알아 차렸다. 반복자는 훨씬 안정적입니다. :) - Ja͢ck
  • 하지만 그들은 참조와 함께 사용할 수 없습니다 ... :( - Baba

3

에서PHP.net

current () 함수는 단순히 배열 요소의 값을 반환합니다.   현재 내부 포인터가 가리키고 있습니다. 그것은하지 않는다.   어떤 식 으로든 포인터를 움직이십시오.

다음 : 사용다음 것()

$list = array("A", "B", "C","D");
foreach ($list as $var) {
    print(current($list));
    next($list);
}

참고 : foreach 포인터를 배열의 두 번째 요소로 이동 한 때문에 첫 번째 요소가 인쇄되지 않습니다 :)

이 예제는 완전한 동작을 설명합니다 :

$list = array("A", "B", "C","D");
foreach ($list as $var) {
   if(!isset($a)) reset($list); $a = 'isset';
   print(current($list));
   next($list);
}

출력은 ABCD입니다.

다음 사항에 유의하십시오.

As foreach relies on the internal array pointer changing it within the loop 
may lead to unexpected behavior.

각각


편집 : 나는 또한 내 새로운 혼란 찾기를 공유하고 싶습니다!

예제 1 :

$list = array("A", "B", "C","D");
$list_copy = $list;
foreach ($list as $key => $val) {
  current($list_copy);
  echo current($list);
  //next($list);
}

출력 : AAAA

예 2 :

$list = array("A", "B", "C","D");
$list_copy = $list;
foreach ($list as $key => $val) {
  current($list_copy);
  echo current($list);
  next($list);
}

출력 : ABCD

foreach 내부에서 current () 함수를 호출 할 때 다른 배열에도 foreach 동작에 영향을줍니다 ...

예 3 :

$list = array("A", "B", "C","D");

$refcopy = &$list;

foreach ($list as $key => $val) {
  if(!isset($a)) { $a = 'isset'; reset($list); }
  echo current($list);
  next($list);
}

출력 : ACD (WOW!누락)

예 : 4

$list = array("A", "B", "C","D");

$refcopy = &$list;

foreach ($list as $key => $val) {
  echo current($list);
  next($list);
}

출력 : BCD

foreach 루프에서 어떤 일이 일어날 지 정확히 결정할 수 없습니다 !!!


2

글쎄, 나는 이것이 실제적인 단서를 가지고 있지는 않지만, 과제가 평가 / 처리되는 방식과 관련이 있다고 생각된다. 재미를 위해 나는 이것을 시도했고 그것은 또 다른 결과를 낳았다.incorrect행동:

$arr = array('A', 'B', 'C', 'D');
function itm($val) {
    return current($val);
}

foreach ($arr as $item) {
    print itm($arr);
}

결과 : BCDA

그래서 내 추측은 여기서 일어나는 일은 함수 호출이 현재의 평가를 ~ 올바른 방식으로 강제 실행한다는 것입니다. 또한 ABCD 대신 BCDA를 얻는 이유는 내부 포인터가 처음에 증가하고 (B를 가리키고) EN에서 다시 A로 리셋되기 때문일 것입니다.

이 줄을PHP doc:

할당은 원래 변수를 새 값으로 복사 (값에 의한 대입)하므로 하나에 대한 변경 사항은 다른 변수에 영향을 미치지 않습니다. 밀집한 루프 안에 큰 배열과 같은 것을 복사해야하는 경우에도 관련성이 있습니다.

나는 이것이 정말로 대답으로 간주되지 않지만 나는 당신의 질문을 좋아했고 조금 기여하고 싶다고 생각합니다.


  • 좋은 .... 이것은 복잡해지고 있습니다 ... 좋은 관찰 - Baba
  • 사실, 소프트 카피 논쟁이 이제는 무효이기 때문에 나는 생각한다. - Daniel Figueroa
  • 질문에 당신의 관찰을 추가 할 것입니다. 이것은 열쇠입니다 ... 관찰 - Baba
  • @ 다니엘 피구로아 나는 그렇게 생각한다. 방금 전달하려고했습니다.$arr을 기준으로하여BBBB.codepad.viper-7.com/6MxdFr - Leri

1

배열의 복사본이 foreach에 의해 만들어졌다하더라도 AAAA를 얻었지만 현재의 PHP 안정 버전에서는 그렇지 못합니다.

나는이 질문에 대한 답을 찾지 못했기 때문에 설명하려고한다.

첫 번째 반복 전에foreach $list실제로 복사되지 않습니다. 만참조 카운팅$list따라서 첫 번째 반복에서 첫 번째 값은$list에서 복사됩니다.$var포인터는 두 번째 요소로 이동합니다.$list실제 사본$list될 것입니다. 그래서 전화 할 때current포인터가 두 번째 요소를 가리 키지 만 두 번째 이상의 반복에서는 포인터가 수정되지 않으므로실제 사본$list존재한다current항상 두 번째 요소를 출력합니다.

편집하다:

나는 함께 놀았다.debug_zval_dump이 예기치 않은 동작을 이해하려면

<pre>

<?php


$list = array("A", "B", "C","D");

echo '<h1>Ref count before entering foreach:</h1><br>';
debug_zval_dump($list); echo '<br><br>';

$i = 0;
echo '<h1>Ref count in foreach:</h1><br>';
foreach ($list as $var) {
    $i++;
    echo '<b>Iteration #'.$i.':</b> ';
    debug_zval_dump($list);
    echo '<br>';
}

$list = array("A", "B", "C","D"); //re-assign array to avoid confusion

echo '<h1>Ref count before entering foreach that calls method "item" and passes array by value:</h1><br>';
debug_zval_dump($list);
$i = 0;
echo '<h1>Ref count in foreach that calls method "item" and passes array by value:</h1><br>';
foreach ( $list as $var ) {
    $i++;
    item($list, $i);
}

function item($list, $i) {
    echo '<b>Iteration #'.$i.':</b> ';
    debug_zval_dump($list);
}

$list = array("A", "B", "C","D"); //re-assign array to avoid confusion

echo '<h1>Ref count before entering foreach that calls method "item" and passes array by reference:</h1><br>';
debug_zval_dump($list);
$i = 0;
echo '<h1>Ref count in foreach that calls method "item" and passes array by reference:</h1><br>';
foreach ( $list as $var ) {
    $i++;
    itemWithRef($list, $i);
}

function itemWithRef(&$list, $i) {
    echo '<b>Iteration #'.$i.':</b> ';
    debug_zval_dump($list);
}

그리고 다음과 같은 결과를 얻었습니다 :

Ref count before entering foreach:
array(4) refcount(2){ [0]=> string(1) "A" refcount(1) [1]=> string(1) "B" refcount(1) [2]=> string(1) "C" refcount(1) [3]=> string(1) "D" refcount(1) }

Ref count in foreach:
Iteration #1: array(4) refcount(3){ [0]=> string(1) "A" refcount(2) [1]=> string(1) "B" refcount(1) [2]=> string(1) "C" refcount(1) [3]=> string(1) "D" refcount(1) }
Iteration #2: array(4) refcount(3){ [0]=> string(1) "A" refcount(1) [1]=> string(1) "B" refcount(2) [2]=> string(1) "C" refcount(1) [3]=> string(1) "D" refcount(1) }
Iteration #3: array(4) refcount(3){ [0]=> string(1) "A" refcount(1) [1]=> string(1) "B" refcount(1) [2]=> string(1) "C" refcount(2) [3]=> string(1) "D" refcount(1) }
Iteration #4: array(4) refcount(3){ [0]=> string(1) "A" refcount(1) [1]=> string(1) "B" refcount(1) [2]=> string(1) "C" refcount(1) [3]=> string(1) "D" refcount(2) }
Ref count before entering foreach that calls method "item" and passes array by value:
array(4) refcount(2){ [0]=> string(1) "A" refcount(1) [1]=> string(1) "B" refcount(1) [2]=> string(1) "C" refcount(1) [3]=> string(1) "D" refcount(1) } Ref count in foreach that calls method "item" and passes array by value:
Iteration #1: array(4) refcount(5){ [0]=> string(1) "A" refcount(2) [1]=> string(1) "B" refcount(1) [2]=> string(1) "C" refcount(1) [3]=> string(1) "D" refcount(1) } Iteration #2: array(4) refcount(5){ [0]=> string(1) "A" refcount(1) [1]=> string(1) "B" refcount(2) [2]=> string(1) "C" refcount(1) [3]=> string(1) "D" refcount(1) } Iteration #3: array(4) refcount(5){ [0]=> string(1) "A" refcount(1) [1]=> string(1) "B" refcount(1) [2]=> string(1) "C" refcount(2) [3]=> string(1) "D" refcount(1) } Iteration #4: array(4) refcount(5){ [0]=> string(1) "A" refcount(1) [1]=> string(1) "B" refcount(1) [2]=> string(1) "C" refcount(1) [3]=> string(1) "D" refcount(2) } Ref count before entering foreach that calls method "item" and passes array by reference:
array(4) refcount(2){ [0]=> string(1) "A" refcount(1) [1]=> string(1) "B" refcount(1) [2]=> string(1) "C" refcount(1) [3]=> string(1) "D" refcount(1) } Ref count in foreach that calls method "item" and passes array by reference:
Iteration #1: array(4) refcount(1){ [0]=> string(1) "A" refcount(4) [1]=> string(1) "B" refcount(3) [2]=> string(1) "C" refcount(3) [3]=> string(1) "D" refcount(3) } Iteration #2: array(4) refcount(1){ [0]=> string(1) "A" refcount(3) [1]=> string(1) "B" refcount(4) [2]=> string(1) "C" refcount(3) [3]=> string(1) "D" refcount(3) } Iteration #3: array(4) refcount(1){ [0]=> string(1) "A" refcount(3) [1]=> string(1) "B" refcount(3) [2]=> string(1) "C" refcount(4) [3]=> string(1) "D" refcount(3) } Iteration #4: array(4) refcount(1){ [0]=> string(1) "A" refcount(3) [1]=> string(1) "B" refcount(3) [2]=> string(1) "C" refcount(3) [3]=> string(1) "D" refcount(4) }

출력은 약간 혼란 스럽습니다.

첫 번째 예에서foreach내부 복사본을 만들었습니다.$list그래서 참조 카운트는 2였습니다 (결과에서 4debug_zval_dump하나 추가refCount). 두 번째 예제 (값 전달)refCount3로 증가했습니다.$list기능을 위해 복사되었습니다. 세 번째 예에서 count는 1로 유지되었으므로$list가치에 의해 전달되었습니다. 왜 그런지 깨달을 시간이 필요합니다. 이 결과 공유에서 포인트를 얻으면.

내가 말할 수있는 것은 우리가 값으로 배열을 통과했을 때foreach반복되는 배열을 전달하고 있었지만 참조로 전달되면기발한 $list. 문제는 왜foreach그 배열을 지나가는거야?


  • + 좋지만 볼 필요가 있습니다.stackoverflow.com/a/14849560/1226894 - Baba
  • @Baba 네, 본적이 있습니다 " wtfphp "라고 생각합니다. - Leri
  • 스스로 믿을 수는 없다. - Baba
  • 함수에서 루프를 돌리는 것이 3 가지 다른 결과를 제공한다는 것을 알고 있습니까?3v4l.org/1aUpd반복자는 같은 결과를 준다.3v4l.org/ViCNn - Baba
  • @Baba 몰랐어. 이 후 나는 실제 애플 리케이션에서 이러한 종류의 구조를 절대 사용해서는 안된다는 것을 훨씬 확신한다. 그것은 나중에 엄청난 머리 통증이 될 것입니다. 행동은 단순히 정의되지 않습니다. - Leri

1

거짓말 일 경우 사용하는 코드. 말 그대로 문자가 같은 코드처럼 보일 수도 있지만 변수는 그렇지 않습니다 (http://3v4l.org/jainJ).

실제 질문에 답하기 위해 일관된 결과를 얻으려면 올바른 도구를 사용하십시오.

배열 값을 가진 변수가 필요하면 다음과 같이 할당하십시오.

$list = array(....);

현재 값을 가져와야하는 경우배열, 사용전에그만큼foreach:

$current = current($list);

내부foreach이 변수 이름은 같을 수 있지만 값이 다를 수 있습니다 (상상해보십시오!).

필요한 경우흐름각 반복 당 가치, 그것을 사용하십시오 :

foreach ($list as $current) {
    ...
}

만나다$current?

오 이런, 그래, 그렇게 쉬웠다. 잠깐, 이미 일관된 결과가 있습니다. 오, 그리고 나 자신을 속일 수 없다는 것이 쉬운 일이었습니다. 예! ;)

로그의 경우 : 변수를 함수 매개 변수로 전달하면이를 새 변수로 만듭니다. 심지어 참조 (즉, 설명).

확실하지 않은 경우 PHP 참조를 사용하지 마십시오. 또는 심지어 변수 :http://3v4l.org/6p5nZ


  • + lol .... 좋은 해결 방법 - Baba

0

위대한 지적. 하지만 그것은 PHP의 다른 버전으로 메모리 가리키는 문제가 보인다. 또한흐름어디에서나 증가 (탐색)하지 않은 현재 위치 만 제공하므로 올바른 결과를 얻지 못합니다. 다른 방법으로 PHP의 다른 버전을 다음과 시작점을 해석하는 방법으로 해결할 수 있습니다.다시 놓기루프 안에 몇 가지 조건이 있습니다. (그런데 루핑하고 현재를 사용하여 다음 prev는 이미 var에 객체가있는 것처럼 좋은 방법이 아닙니다 :) 당신이 선택한 것은 무엇입니까? 이것이 작동하도록하는 한 가지 방법입니다.

<?php
$list = array("A", "B", "C","D");
$flag =0;
foreach ($list as $var) {
    if($flag==0)
    {   
        reset($list);
        $flag=1;
    }
    print(current($list));
    next($list);
}

출력은 ABCD입니다. 에서보기http://3v4l.org/5Hm5Y


-1

$list = array("A", "B", "C","D");
foreach ($list as $var) {
    echo $var;
}

그것을해야한다.


  • 질문에 언급 된대로 문제를 검토하십시오. 질문은 다음과 같은 비정상적인 행동에 관한 것입니다.currentforeach 내부에서 특정 출력을 얻는 방법이 아닙니다. - Charles

-1

사용 당신은 이미 일어난 일을 알고 있습니다!

$list = array('A', 'B', 'C','D');
foreach ($list as $var) {
 var_dump(current($list));
}

당신을 도울 수 있습니다!

연결된 질문


관련된 질문

최근 질문