복붙노트

PHP에서 RecursiveIteratorIterator는 어떻게 작동합니까?

PHP

PHP에서 RecursiveIteratorIterator는 어떻게 작동합니까?

RecursiveIteratorIterator는 어떻게 작동합니까?

PHP 매뉴얼에는 많은 문서 나 설명이 없습니다. IteratorIterator와 RecursiveIteratorIterator의 차이점은 무엇입니까?

해결법

  1. ==============================

    1.RecursiveIteratorIterator는 트리 순회를 구현하는 구상 적 반복자입니다. 프로그래머는 RecursiveIterator 인터페이스를 구현하는 컨테이너 객체를 탐색 할 수 있습니다. 반복기의 일반적인 원칙, 유형, 의미 및 패턴은 Wikipedia의 Iterator를 참조하십시오.

    RecursiveIteratorIterator는 트리 순회를 구현하는 구상 적 반복자입니다. 프로그래머는 RecursiveIterator 인터페이스를 구현하는 컨테이너 객체를 탐색 할 수 있습니다. 반복기의 일반적인 원칙, 유형, 의미 및 패턴은 Wikipedia의 Iterator를 참조하십시오.

    선형 순서로 오브젝트 traversal를 구현하는 반복자 Iterator와의 차이로, RecursiveIteratorIterator는 오브젝트의 순서 붙일 수 있었던 트리 내의 모든 노드에 대해서 루핑을 가능하게 해, 생성자은 RecursiveIterator를 취합니다.

    즉, RecursiveIteratorIterator를 사용하면 트리를 반복 할 수 있으므로 IteratorIterator를 사용하면 목록을 반복 할 수 있습니다. 곧 아래의 코드 예제를 통해이를 보여줍니다.

    기술적으로 이는 모든 노드의 자식 (있는 경우)을 통과하여 선형성을 깨뜨리는 방식으로 작동합니다. 이것은 노드의 모든 자식이 다시 RecursiveIterator이기 때문에 가능합니다. 최상위 반복자는 내부적으로 다른 RecursiveIterator를 깊이 쌓고, 탐색을 위해 현재 활성 하위 반복자에 대한 포인터를 유지합니다.

    이렇게하면 트리의 모든 노드를 방문 할 수 있습니다.

    기본이되는 원칙은 IteratorIterator와 같습니다. 인터페이스는 반복 유형을 지정하고 기본 반복자 클래스는 이러한 의미의 구현입니다. 아래 예와 비교하면 foreach를 사용한 선형 루핑의 경우 새 Iterator를 정의해야하는 경우가 아니라면 구현 정보를 많이 고려하지 않는 것이 좋습니다 (예 : 특정 유형 자체가 Traversable을 구현하지 않는 경우).

    재귀 적 트래버스의 경우 - 이미 재귀 적 트래버스 반복이있는 사전 정의 된 Traversal을 사용하지 않는 한, 일반적으로 기존의 RecursiveIteratorIterator 반복을 인스턴스화하거나이 트래버스 반복 유형을 가지기 위해 Traversable 자신의 재귀 트래버스 반복을 작성해야합니다 foreach와.

    짧은 기술적 차이점 :

    요약하면 : RecursiveIterator는 반복자 (즉, RecursiveIterator)에서 작동하는 구체적인 반복 유형 (반복 반복)입니다. 이는 IteratorIerator와 동일한 기본 원칙이지만 반복 유형은 다릅니다 (선형 순서).

    이상적으로 자신 만의 세트를 만들 수도 있습니다. 필요한 유일한 것은 iterator가 Iterator 또는 IteratorAggregate를 통해 가능한 Traversable을 구현한다는 것입니다. 그런 다음 foreach와 함께 사용할 수 있습니다. 예를 들어 컨테이너 객체에 대한 iteration 인터페이스와 함께 일종의 삼항 트리 탐색 순회 반복 객체가 있습니다.

    추상적이지 않은 실제 사례를 살펴 보겠습니다. 인터페이스들 사이에서, 구체적인 반복자, 컨테이너 객체 및 반복 의미론은 그다지 좋지 않습니다.

    디렉토리 목록을 예로 들어 보겠습니다. 디스크에 다음과 같은 파일 및 디렉토리 트리가 있다는 것을 고려하십시오.

    선형 순서를 가진 반복자가 최상위 폴더와 파일 (단일 디렉토리 목록)을 순회하는 동안 재귀 적 반복자는 하위 폴더를 통과하여 모든 폴더와 파일 (하위 디렉토리 목록이있는 디렉토리)을 나열합니다.

    Non-Recursive        Recursive
    =============        =========
    
       [tree]            [tree]
        ├ dirA            ├ dirA
        └ fileA           │ ├ dirB
                          │ │ └ fileD
                          │ ├ fileB
                          │ └ fileC
                          └ fileA
    

    이것을 디렉토리 트리를 탐색하기위한 재귀를 수행하지 않는 IteratorIterator와 쉽게 비교할 수 있습니다. 재귀 적 목록처럼 트리를 탐색 할 수있는 RecursiveIteratorIterator입니다.

    foreach가 반복 처리 할 수 ​​있도록 (듯이), Traversable를 구현하는 DirectoryIterator를 가지는 매우 기본적인 예를 다음에 나타냅니다.

    $path = 'tree';
    $dir  = new DirectoryIterator($path);
    
    echo "[$path]\n";
    foreach ($dir as $file) {
        echo " ├ $file\n";
    }
    

    위의 디렉토리 구조에 대한 예제 출력은 다음과 같습니다.

    [tree]
     ├ .
     ├ ..
     ├ dirA
     ├ fileA
    

    보시다시피, 아직 IteratorIterator 또는 RecursiveIteratorIterator를 사용하고 있지 않습니다. 대신 Traversable 인터페이스에서 작동하는 foreach를 사용합니다.

    foreach는 기본적으로 선형 순서라는 반복 유형 만 알고 있으므로 반복 유형을 명시 적으로 지정할 수 있습니다. 언뜻보기에는 너무 복잡해 보일 수도 있지만 데모 용으로 (나중에 RecursiveIteratorIterator와의 차이점을 더 쉽게 보이기 위해), 디렉토리 목록에 대한 반복의 IteratorIterator 유형을 명시 적으로 지정하여 선형 유형의 반복을 지정할 수 있습니다.

    $files = new IteratorIterator($dir);
    
    echo "[$path]\n";
    foreach ($files as $file) {
        echo " ├ $file\n";
    }
    

    이 예제는 첫 번째 예제와 거의 동일하지만, 차이점은 $ files가 이제 Traversable $ dir에 대한 반복의 IteratorIterator 유형이라는 것입니다.

    $files = new IteratorIterator($dir);
    

    평소처럼 반복 작업은 foreach에 의해 수행됩니다.

    foreach ($files as $file) {
    

    출력은 정확히 동일합니다. 그래서 다른 무엇입니까? foreach 내에서 사용되는 객체가 다릅니다. 첫 번째 예제에서 두 번째 예제에서는 DirectoryIterator이고 IteratorIterator입니다. 이것은 반복자가 가지는 유연성을 보여줍니다 : 서로를 대체 할 수 있습니다. foreach 내부의 코드는 예상대로 계속 작동합니다.

    하위 디렉토리를 포함하여 전체 목록을 가져올 수 있습니다.

    이제 반복 유형을 지정 했으므로 반복 유형을 다른 유형으로 변경하는 것을 고려해 보겠습니다.

    우리는 첫 번째 레벨뿐만 아니라 전체 트리를 지금 탐색해야한다는 것을 알고 있습니다. 간단한 foreach를 사용하려면 iterator의 다른 유형 인 RecursiveIteratorIterator가 필요하다. 그리고 그 하나만 RecursiveIterator 인터페이스를 가진 컨테이너 객체를 반복 할 수 있습니다.

    인터페이스는 계약입니다. 이 클래스를 구현하는 모든 클래스는, RecursiveIteratorIterator와 함께 사용할 수 있습니다. 그러한 클래스의 예는, DirectoryIterator의 재귀 적 변형과 같은 RecursiveDirectoryIterator입니다.

    I-word를 사용하여 다른 문장을 작성하기 전에 첫 번째 코드 예제를 보겠습니다.

    $dir  = new RecursiveDirectoryIterator($path);
    
    echo "[$path]\n";
    foreach ($dir as $file) {
        echo " ├ $file\n";
    }
    

    이 세 번째 예제는 첫 번째 예제와 거의 동일하지만 다른 결과를 생성합니다.

    [tree]
     ├ tree\.
     ├ tree\..
     ├ tree\dirA
     ├ tree\fileA
    

    좋아, 그다지 다르지는 않지만 파일 이름 앞에 경로 이름이 포함되어 있지만 나머지도 비슷하게 보입니다.

    예제에서 알 수 있듯이, 디렉토리 객체조차도 이미 RecursiveIterator 인터페이스를 구현하고 있습니다 만, foreach가 전체 디렉토리 트리를 가로 지르도록하기에는 아직 충분하지 않습니다. 이것에 의해, RecursiveIteratorIterator가 동작합니다. 예제 4는 다음과 같은 방법을 보여줍니다.

    $files = new RecursiveIteratorIterator($dir);
    
    echo "[$path]\n";
    foreach ($files as $file) {
        echo " ├ $file\n";
    }
    

    이전 $ dir 객체 대신 RecursiveIteratorIterator를 사용하면 foreach가 모든 파일과 디렉토리를 재귀 적으로 탐색합니다. 그러면 개체 반복의 유형이 지금 지정되었으므로 모든 파일이 나열됩니다.

    [tree]
     ├ tree\.
     ├ tree\..
     ├ tree\dirA\.
     ├ tree\dirA\..
     ├ tree\dirA\dirB\.
     ├ tree\dirA\dirB\..
     ├ tree\dirA\dirB\fileD
     ├ tree\dirA\fileB
     ├ tree\dirA\fileC
     ├ tree\fileA
    

    이것은 평면과 트리 순회의 차이를 이미 입증해야합니다. RecursiveIteratorIterator는 트리의 구조를 요소의리스트로서 횡단 (traverse) 할 수 있습니다. 더 많은 정보가 있기 때문에 (반복이 현재 수행하는 레벨과 같음) iterator 객체에 반복적으로 액세스하면서 출력을 들여 쓰기하는 등의 반복 객체에 액세스 할 수 있습니다.

    echo "[$path]\n";
    foreach ($files as $file) {
        $indent = str_repeat('   ', $files->getDepth());
        echo $indent, " ├ $file\n";
    }
    

    그리고 예제 5의 출력 :

    [tree]
     ├ tree\.
     ├ tree\..
        ├ tree\dirA\.
        ├ tree\dirA\..
           ├ tree\dirA\dirB\.
           ├ tree\dirA\dirB\..
           ├ tree\dirA\dirB\fileD
        ├ tree\dirA\fileB
        ├ tree\dirA\fileC
     ├ tree\fileA
    

    물론 이것은 미인 대회에서 우승하지는 못하지만 재귀 적 반복기를 사용하면 키와 값의 선형 순서보다 많은 정보를 얻을 수 있다는 것을 알 수 있습니다. foreach조차도 이런 종류의 선형성을 표현할 수 있습니다. 반복자 자체에 액세스하면 더 많은 정보를 얻을 수 있습니다.

    메타 정보와 마찬가지로 트리를 탐색하여 출력을 정렬하는 방법도 다양합니다. 이것은 RecursiveIteratorIterator의 Mode이며 생성자를 사용하여 설정할 수 있습니다.

    다음 예제는 RecursiveDirectoryIterator에 도트 항목 (. 및 ..)을 필요없는 것처럼 제거하도록 지시합니다. 또한 재귀 모드는 하위 요소 (하위 디렉토리의 파일 및 하위 subdir) 앞에 부모 요소 (하위 디렉토리)를 먼저 가져 오도록 변경됩니다 (SELF_FIRST).

    $dir  = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
    $files = new RecursiveIteratorIterator($dir, RecursiveIteratorIterator::SELF_FIRST);
    
    echo "[$path]\n";
    foreach ($files as $file) {
        $indent = str_repeat('   ', $files->getDepth());
        echo $indent, " ├ $file\n";
    }
    

    이전 출력 결과와 비교해 보면 출력 결과에는 올바르게 나열된 하위 디렉토리 항목이 표시됩니다.

    [tree]
     ├ tree\dirA
        ├ tree\dirA\dirB
           ├ tree\dirA\dirB\fileD
        ├ tree\dirA\fileB
        ├ tree\dirA\fileC
     ├ tree\fileA
    

    따라서 재귀 모드는 트리의 분기 나 리프가 반환되는 시점과시기를 제어합니다. 예를 들면 다음과 같습니다.

    두 가지 다른 모드로 예제 5의 출력 :

      LEAVES_ONLY                           CHILD_FIRST
    
      [tree]                                [tree]
             ├ tree\dirA\dirB\fileD                ├ tree\dirA\dirB\fileD
          ├ tree\dirA\fileB                     ├ tree\dirA\dirB
          ├ tree\dirA\fileC                     ├ tree\dirA\fileB
       ├ tree\fileA                             ├ tree\dirA\fileC
                                            ├ tree\dirA
                                            ├ tree\fileA
    

    표준 트래버스와 비교할 때이 모든 것을 사용할 수 없습니다. 반복적 인 반복은 머리를 감싸 줘야 할 때 좀 더 복잡하지만 iterator처럼 작동하고 foreach에 넣고 수행하기 때문에 사용하기 쉽습니다.

    나는 이것이 하나의 대답에 대한 충분한 예라고 생각한다. 이 요지에서 멋진 원시 ASCII 코드를 보여주는 예제와 함께 전체 소스 코드를 찾을 수 있습니다 : https://gist.github.com/3599532

    예제 5는 사용 가능한 반복자의 상태에 대한 메타 정보가 있음을 보여 줬다. 그러나 이것은 foreach 반복 내에서 의도적으로 시연되었습니다. 실생활에서는 자연스럽게 재귀 적 반복기 내부에 속합니다.

    더 좋은 예는 RecursiveTreeIterator입니다. 들여 쓰기, 접두사 처리 등을 처리합니다. 다음 코드 단편을 참조하십시오.

    $dir   = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
    $lines = new RecursiveTreeIterator($dir);
    $unicodeTreePrefix($lines);
    echo "[$path]\n", implode("\n", iterator_to_array($lines));
    

    RecursiveTreeIterator는 1 줄 단위로 동작하도록 (듯이)되어 있습니다 만, 출력은 약간 간단한 문제만으로도 꽤 간단합니다.

    [tree]
     ├ tree\dirA
     │ ├ tree\dirA\dirB
     │ │ └ tree\dirA\dirB\fileD
     │ ├ tree\dirA\fileB
     │ └ tree\dirA\fileC
     └ tree\fileA
    

    RecursiveDirectoryIterator와 함께 사용하면 파일 이름뿐만 아니라 전체 경로 이름을 표시합니다. 나머지는 좋아 보인다. 이것은 파일 이름이 SplFileInfo에 의해 생성되기 때문입니다. 대신 기본 이름으로 표시되어야합니다. 원하는 출력은 다음과 같습니다.

    /// Solved ///
    
    [tree]
     ├ dirA
     │ ├ dirB
     │ │ └ fileD
     │ ├ fileB
     │ └ fileC
     └ fileA
    

    RecursiveDirectoryIterator 대신에 RecursiveTreeIterator와 함께 사용할 수있는 데코레이터 클래스를 만듭니다. 경로 이름 대신 현재 SplFileInfo의 기본 이름을 제공해야합니다. 최종 코드 조각은 다음과 같이 보일 수 있습니다.

    $lines = new RecursiveTreeIterator(
        new DiyRecursiveDecorator($dir)
    );
    $unicodeTreePrefix($lines);
    echo "[$path]\n", implode("\n", iterator_to_array($lines));
    

    $ unicodeTreePrefix를 포함하는 이러한 조각은 부록의 "요점 : 직접 작성 : 반복적 인 반복자를 줄 단위로 만들기"의 요점의 일부입니다.

  2. ==============================

    2.이 두 반복자의 차이점을 이해하려면 먼저 사용 된 명명 규칙과 "반복적"반복자의 의미에 대해 약간 이해해야합니다.

    이 두 반복자의 차이점을 이해하려면 먼저 사용 된 명명 규칙과 "반복적"반복자의 의미에 대해 약간 이해해야합니다.

    PHP에는 ArrayIterator 및 FilesystemIterator와 같은 "재귀 적"이터레이터가 없습니다. RecursiveArrayIterator 및 RecursiveDirectoryIterator와 같은 "재귀"반복기도 있습니다. 후자는 그들을 드릴 다운 할 수있는 방법을 가지고 있지만, 그렇지 않은 방법은 있습니다.

    이 반복자의 인스턴스가 자체적으로 반복되는 경우에도 재귀 적으로 반복되는 경우에도 하위 디렉토리가있는 중첩 배열이나 디렉토리를 루핑하는 경우에도 값은 "최상위"수준에서만옵니다.

    재귀 적 반복자는 (hasChildren (), getChildren ()을 통해) 재귀 적 동작을 구현하지만이를 이용하지는 않습니다.

    재귀 적 반복기를 "재귀 적"반복기라고 생각하면 재귀 적으로 반복 될 수 있지만이 클래스 중 하나의 인스턴스를 반복하면 단순히 수행 할 수 없습니다. 재귀 적 동작을 이용하려면 계속해서 읽으십시오.

    이 곳에서 RecursiveIteratorIterator가 재생됩니다. 그것은 "재귀 적"반복자를 호출하여 정상적인 평면 루프로 구조를 드릴 다운하는 방법에 대한 지식을 가지고 있습니다. 재귀 적 행동을 행동으로 옮깁니다. 본질적으로 iterator의 각 값을 단계별로 실행하고, 반복되는 "자식"이 있는지 여부를 확인하고 해당 자식 콜렉션으로 들어갔다 나오는 것을 확인합니다. foreach에 RecursiveIteratorIterator의 인스턴스를 고정시키고, 구조에 다이빙 (dives)하여 그렇게 할 필요가 없습니다.

    RecursiveIteratorIterator가 사용되지 않은 경우 재귀 동작을 악용하기 위해 자체 재귀 루프를 작성하고 "반복 가능한"반복자의 hasChildren () 및 getChildren ()을 사용하여 확인해야합니다.

    RecursiveIteratorIterator에 대한 간단한 개요입니다. IteratorIterator와 다른 점은 무엇입니까? 기본적으로 고양이와 나무의 차이는 무엇입니까? 둘 다 같은 백과 사전 (또는 매뉴얼, iterator)에 나타나기 때문에 둘 사이에 혼란스러워해야한다는 것을 의미하지는 않습니다.

    IteratorIterator의 작업은, Traversable 오브젝트를 취득 해, Iterator 인터페이스를 채우도록 (듯이) 랩하는 것입니다. 이를 위해 반복자가 아닌 객체에 반복자 특정 비헤이비어를 적용 할 수 있어야합니다.

    실용적인 예를 들면 DatePeriod 클래스는 Traversable이지만 Iterator는 아닙니다. 따라서 foreach ()를 사용하여 값을 반복 할 수 있지만 일반적으로 필터링과 같은 반복기를 사용하여 수행 할 수있는 다른 작업은 수행 할 수 없습니다.

    작업 : 다음 4 주 동안 월요일, 수요일 및 금요일에 반복합니다.

    예, 이것은 DatePeriod를 foreaching하고 루프 내에서 if ()를 사용하면 간단합니다. 하지만이 예제의 요점은 아닙니다!

    $period = new DatePeriod(new DateTime, new DateInterval('P1D'), 28);
    $dates  = new CallbackFilterIterator($period, function ($date) {
        return in_array($date->format('l'), array('Monday', 'Wednesday', 'Friday'));
    });
    foreach ($dates as $date) { … }
    

    CallbackFilterIterator가 Iterator 인터페이스를 구현하는 클래스의 인스턴스를 기대하기 때문에 위의 코드 조각은 작동하지 않습니다. DatePeriod는 그렇지 않습니다. 그러나, Traversable이기 때문에, IteratorIterator를 사용하면 (자), 그러한 요구를 쉽게 채울 수가 있습니다.

    $period = new IteratorIterator(new DatePeriod(…));
    

    보시다시피이 클래스는 반복기 클래스 나 재귀를 반복하는 것과는 아무런 관련이 없으며 IteratorIterator와 RecursiveIteratorIterator의 차이점이 있습니다.

    RecursiveIteratorIterator는 재귀 적 반복자 ( "recursible"iterator)를 반복 처리하여 사용 가능한 재귀 적 동작을 악용합니다.

    IteratorIterator는 iterator가 아닌 반복자 객체에 Iterator 비헤이비어를 적용하기위한 것입니다.

  3. ==============================

    3.RecursiveDirectoryIterator는 파일 이름뿐만 아니라 전체 경로 이름을 표시합니다. 나머지는 좋아 보인다. 이것은 파일 이름이 SplFileInfo에 의해 생성되기 때문입니다. 대신 기본 이름으로 표시되어야합니다. 원하는 출력은 다음과 같습니다.

    RecursiveDirectoryIterator는 파일 이름뿐만 아니라 전체 경로 이름을 표시합니다. 나머지는 좋아 보인다. 이것은 파일 이름이 SplFileInfo에 의해 생성되기 때문입니다. 대신 기본 이름으로 표시되어야합니다. 원하는 출력은 다음과 같습니다.

    $path =__DIR__;
    $dir = new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS);
    $files = new RecursiveIteratorIterator($dir,RecursiveIteratorIterator::SELF_FIRST);
    while ($files->valid()) {
        $file = $files->current();
        $filename = $file->getFilename();
        $deep = $files->getDepth();
        $indent = str_repeat('│ ', $deep);
        $files->next();
        $valid = $files->valid();
        if ($valid and ($files->getDepth() - 1 == $deep or $files->getDepth() == $deep)) {
            echo $indent, "├ $filename\n";
        } else {
            echo $indent, "└ $filename\n";
        }
    }
    

    산출:

    tree
     ├ dirA
     │ ├ dirB
     │ │ └ fileD
     │ ├ fileB
     │ └ fileC
     └ fileA
    
  4. ==============================

    4.iterator_to_array ()와 함께 사용하면 RecursiveIteratorIterator는 배열을 반복적으로 탐색하여 모든 값을 찾습니다. 그것이 원래 배열을 평평하게한다는 것을 의미합니다.

    iterator_to_array ()와 함께 사용하면 RecursiveIteratorIterator는 배열을 반복적으로 탐색하여 모든 값을 찾습니다. 그것이 원래 배열을 평평하게한다는 것을 의미합니다.

    IteratorIterator는 원래의 계층 구조를 유지합니다.

    이 예제는 차이점을 명확히 보여줍니다 :

    $array = array(
                   'ford',
                   'model' => 'F150',
                   'color' => 'blue', 
                   'options' => array('radio' => 'satellite')
                   );
    
    $recursiveIterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
    var_dump(iterator_to_array($recursiveIterator, true));
    
    $iterator = new IteratorIterator(new ArrayIterator($array));
    var_dump(iterator_to_array($iterator,true));
    
  5. from https://stackoverflow.com/questions/12077177/how-does-recursiveiteratoriterator-work-in-php by cc-by-sa and MIT license