2013-09-02 2 views
12

for ... in ... do을 사용하여 동적 배열을 반복하여 배열에 항목의 사본을 만드시겠습니까? 예를 들면 다음과 같습니다.델파이 동적 배열 반복 및 레코드 복사

type 
    TSomeRecord =record 
    SomeField1 :string; 
    SomeField2 :string; 
    end; 

var 
    list: array of TSomeRecord; 
    item: TSomeRecord; 

begin 
    // Fill array here 
    for item in list do 
    begin 
     // Is item here a copy of the item in the array or a reference to it? 
    end; 
end; 

루프의 항목은 배열의 항목 또는 참조의 사본일까요?

복사본 인 경우 복사본을 만들지 않고 배열을 반복 할 수 있습니까?

감사

AJ

+0

어쨌든 나는 유사한 시나리오에서 레코드 포인터를 구현합니다. 레코드를 전달하고 다른 위치에서 내용을 수정하려면 언제든지 레코드 포인터'PSomeRecord'가 훨씬 효율적으로 작동하므로 복사되는 것에 대해 걱정할 필요가 없습니다. 특히 많은 내용 (많은 메모리)을 보유하고 있다면. –

+1

@ Jerry 그러나 이제는 수명을 명시 적으로 관리해야합니다. –

+0

@David True, 모든 방식의 큰 변화이지만 단일 레코드가 많은 양의 메모리를 소비하는 경우 포인터로 유지하는 것이 가장 좋습니다. –

답변

11

의 루프 변수 A에 대한/루프의 루프가 반복되는 동안 용기에 유지 된 값의 사본이다.

동적 배열의 기본 열거자를 바꿀 수 없으므로 복사본이 아닌 참조를 반환하는 열거자를 만들 수 없습니다. 레코드 내에서 배열을 마무리하는 경우 참조를 반환하는 레코드의 열거자를 만들 수 있습니다.

분명히 복사를 피하려는 경우 전통적인 색인 된 for 루프를 사용할 수 있습니다. 컴파일러는 구현하기 위해 선택하지 않는 이유 위의 문장의 어떤 문서 수없는 것으로 나타납니다 이후


하나는 요청할 수도 있습니다에 대한/루프 오히려 복사본보다 참조를 사용한다. 디자이너 만이 그 질문에 답할 수 있지만, 나는 정당성을 제시 할 수 있습니다.

사용자 지정 열거자를 고려하십시오. 다음과 같이 documentation이 메커니즘을 설명

가 클래스 또는 인터페이스의 구성에 대해 루프 - 더 을 사용하려면 클래스 또는 인터페이스는 소정의 수집 패턴을 구현한다. 컬렉션 패턴을 구현하는 유형은 다음 속성이 있어야합니다

  • 클래스 또는 인터페이스가 GetEnumerator()라는 공공 인스턴스 메소드를 포함해야합니다. GetEnumerator() 메서드는 클래스, 인터페이스 또는 레코드 유형을 반환해야합니다.
  • GetEnumerator()에 의해 반환 된 클래스, 인터페이스 또는 레코드에는 MoveNext()이라는 공용 인스턴스 메서드가 포함되어야합니다. MoveNext() 메서드는 Boolean을 반환해야합니다. for-in 루프는 먼저이 메소드 을 호출하여 컨테이너가 비어 있지 않은지 확인합니다.
  • GetEnumerator()에 의해 반환 된 클래스, 인터페이스 또는 레코드에는 public 인스턴스 인 Current이라는 읽기 전용 속성이 있어야합니다. 유형의 Current 속성은 컬렉션에 포함 된 유형이어야합니다.

사용자 정의 열거는 Current 속성을 통해 컬렉션에서 각 값을 반환합니다. 그리고 그 값이 복사되었음을 의미합니다.

따라서 사용자 지정 열거자는 항상 복사본을 사용합니다.배열 용 내장 열거자가 참조를 사용할 수 있다고 상상해보십시오. 그 결과 두 가지 유형의 열거 자간에 의미있는 차이가 발생합니다. 디자이너가 서로 다른 유형의 열거 자의 의미간에 일관성을 유지하는 것이 확실한 것은 확실합니다.

+0

복사본이 만들어지면 왜 복사본을 수정할 수 없습니까? 예를 들어 item.SomeField1 : = 'Test'는 컴파일 오류를 발생시킵니다. –

+1

간단히 말해서, 언어 정의는 루프 변수를 수정할 수 없다는 것을 선언합니다. –

+1

아마도 이것이 정확히 사본이기 때문에 복사에 대한 변경 사항은 배열에 저장되지 않습니다. – VitaliyG

5

@David는 열거자가 레코드의 복사본이라고 대답했습니다.

그는 또한 레코드 안에 동적 배열을 래핑하면 맞춤식 열거자를 사용할 수 있다고합니다. 여기

그 일의 예입니다 : 그냥 완료 할

program ProjectCustomEnumerator; 

{$APPTYPE CONSOLE} 

type 
    PSomeRecord = ^TSomeRecord; 
    TSomeRecord = record 
    SomeField1 :string; 
    SomeField2 :string; 
    end; 

    TSomeRecordArray = record 
    private type 
    TSomeRecordDynArray = array of TSomeRecord; 
    // For x in .. enumerator 
    TSomeRecordArrayEnumerator = record 
     procedure Create(const AnArray : TSomeRecordDynArray); 
     private 
     FCurrent,FLast : Integer; 
     FArray : TSomeRecordDynArray; 
     function GetCurrent : PSomeRecord; inline; 
     public 
     function MoveNext : Boolean; inline; 
     property Current : PSomeRecord read GetCurrent; 
    end; 
    public 
    List : TSomeRecordDynArray; 
    // Enumerator interface 
    function GetEnumerator : TSomeRecordArrayEnumerator; inline; 
    end; 

procedure TSomeRecordArray.TSomeRecordArrayEnumerator.Create(
    const AnArray: TSomeRecordDynArray); 
begin 
    FCurrent := -1; 
    FLast := Length(AnArray)-1; 
    FArray := AnArray; 
end; 

function TSomeRecordArray.TSomeRecordArrayEnumerator.GetCurrent: PSomeRecord; 
begin 
    Result := @FArray[FCurrent]; 
end; 

function TSomeRecordArray.TSomeRecordArrayEnumerator.MoveNext: Boolean; 
begin 
    Inc(FCurrent); 
    Result := (FCurrent <= FLast); 
end; 

function TSomeRecordArray.GetEnumerator: TSomeRecordArrayEnumerator; 
begin 
    Result.Create(Self.List); 
end; 

var 
    aList : TSomeRecordArray; 
    item : PSomeRecord; 
    i : Integer; 
begin 
    // Fill array here 
    SetLength(aList.List,2); 
    aList.List[0].SomeField1 := 'Ix=0; Field1'; 
    aList.List[0].SomeField2 := 'Ix=0; Field2'; 
    aList.List[1].SomeField1 := 'Ix=1; Field1'; 
    aList.List[1].SomeField2 := 'Ix=1; Field2'; 
    i := -1; 
    for item in aList do 
    begin 
    // Item here a pointer to the item in the array 
    Inc(i); 
    WriteLn('aList index:',i,' Field1:',item^.SomeField1,' Field2:',item^.SomeField2); 
    end; 
    ReadLn; 
end. 

편집

과 의견을 다음, 여기에, 기록의 동적 배열에 대한 일반 컨테이너 예입니다 사용자 지정 열거 자입니다.

program ProjectCustomEnumerator; 

{$APPTYPE CONSOLE} 

type 
    PSomeRecord = ^TSomeRecord; 
    TSomeRecord = record 
    SomeField1 :string; 
    SomeField2 :string; 
    end; 

    TRecordArray<T> = record 
    private type 
    TRecordDynArray = array of T; 
    // For x in .. enumerator 
    TRecordArrayEnumerator = record 
     procedure Initialize(const AnArray : TRecordDynArray); 
     private 
     FCurrent,FLast : Integer; 
     FArray : TRecordDynArray; 
     function GetCurrent : Pointer; inline; 
     public 
     function MoveNext : Boolean; inline; 
     property Current : Pointer read GetCurrent; 
    end; 
    public 
    List : TRecordDynArray; 
    // Enumerator interface 
    function GetEnumerator : TRecordArrayEnumerator; inline; 
    end; 

procedure TRecordArray<T>.TRecordArrayEnumerator.Initialize(
    const AnArray: TRecordDynArray); 
begin 
    FCurrent := -1; 
    FLast := Length(AnArray)-1; 
    FArray := AnArray; 
end; 

function TRecordArray<T>.TRecordArrayEnumerator.GetCurrent: Pointer; 
begin 
    Result := @FArray[FCurrent]; 
end; 

function TRecordArray<T>.TRecordArrayEnumerator.MoveNext: Boolean; 
begin 
    Inc(FCurrent); 
    Result := (FCurrent <= FLast); 
end; 

function TRecordArray<T>.GetEnumerator: TRecordArrayEnumerator; 
begin 
    Result.Initialize(Self.List); 
end; 

var 
    aList : TRecordArray<TSomeRecord>; 
    item : PSomeRecord; 
    i : Integer; 
begin 
    // Fill array here 
    SetLength(aList.List,2); 
    aList.List[0].SomeField1 := 'Ix=0; Field1'; 
    aList.List[0].SomeField2 := 'Ix=0; Field2'; 
    aList.List[1].SomeField1 := 'Ix=1; Field1'; 
    aList.List[1].SomeField2 := 'Ix=1; Field2'; 
    i := -1; 
    for item in aList do 
    begin 
    // Item here a pointer to the item in the array 
    Inc(i); 
    WriteLn('aList index:',i,' Field1:',item^.SomeField1,' Field2:',item^.SomeField2); 
    end; 
    ReadLn; 
end. 
+0

+1 의견. 전체 기록을 일반화하여 모든 유형에 사용할 수 있습니다. 나는 또한 인스턴스 메소드에 Create를 사용하는 것이 혼란 스럽다고 말한다. 생성자와 너무 흡사합니다. 새 열거자를 반환 한 New라는 클래스 함수 나 Initialize라는 인스턴스 메서드가 있습니다. –

+0

@DavidHeffernan,이를 일반화하면 열거자는 'PT =^T;'유형을 반환하지만 루프 항목은'PSomeRecord'로 선언됩니다. '호환되지 않는 타입'에러를 피하는 법? –

+0

'GetCurrent'의 리턴 타입을 Pointer로 선언하면 호환되지 않는 타입 에러가 해결됩니다. 비록 그것이 최선의 해결책인지 확실하지 않습니다. –