2013-09-02 39 views
12

Không lặp qua mảng động bằng cách sử dụng for ... in ... do tạo bản sao của mục trong mảng? Ví dụ:Lặp lại mảng động Delphi và sao chép bản ghi

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; 

Mục trong vòng lặp có phải là bản sao của mục trong mảng hoặc tham chiếu đến mục đó không?

Nếu đó là bản sao có thể lặp qua mảng mà không có bản sao được tạo không?

Cảm ơn,

AJ

+0

Tôi sẽ triển khai con trỏ bản ghi cho mọi trường hợp tương tự. Bất cứ lúc nào bạn muốn truyền các bản ghi xung quanh và sửa đổi nội dung của chúng ở những nơi khác nhau, một con trỏ bản ghi 'PSomeRecord' hoạt động hiệu quả hơn rất nhiều và bạn không phải lo lắng về việc nó được sao chép. Đặc biệt là nếu nó chứa rất nhiều nội dung (rất nhiều bộ nhớ). –

+1

@Jerry Nhưng bây giờ bạn phải quản lý suốt đời –

+0

@ David True, đó là một thay đổi lớn trong cách mọi thứ hoạt động, nhưng như tôi đã nói, nếu một bản ghi duy nhất tiêu thụ một lượng lớn bộ nhớ, tốt nhất là giữ chúng trong con trỏ. –

Trả lời

11

Biến vòng một cho/trong vòng lặp là một bản sao của giá trị được tổ chức bởi các container trên đó vòng lặp được lặp lại.

Vì bạn không thể thay thế điều tra mặc định cho mảng động, không có cách nào để bạn tạo một điều tra viên trả về tham chiếu thay vì bản sao. Nếu bạn đã kết thúc mảng bên trong một bản ghi, bạn có thể tạo một điều tra viên cho bản ghi sẽ trả về các tham chiếu.

Rõ ràng bạn có thể sử dụng một vòng lặp được lập chỉ mục truyền thống nếu bạn muốn tránh tạo bản sao.


Người ta có thể hỏi, vì dường như không có tài liệu nào về tuyên bố trên, tại sao trình biên dịch không chọn thực hiện như vậy cho/trong vòng sử dụng tham chiếu thay vì bản sao. Chỉ những nhà thiết kế mới có thể trả lời chắc chắn, nhưng tôi có thể đưa ra một sự biện minh.

Hãy xem xét điều tra viên tùy chỉnh. Các documentation mô tả các cơ chế như sau:

Để sử dụng cho-in loop xây dựng trên một lớp hoặc giao diện, lớp hoặc giao diện phải thực hiện một mô hình thu quy định. Một loại mà thực hiện mô hình bộ sưu tập phải có thuộc tính sau:

  • Lớp hoặc giao diện phải có một phương pháp dụ nào gọi GetEnumerator(). Phương thức GetEnumerator() phải trả về một lớp, giao diện hoặc loại bản ghi.
  • Lớp, giao diện hoặc bản ghi được trả về bởi GetEnumerator() phải chứa phương thức công khai được gọi là MoveNext(). Phương thức MoveNext() phải trả lại Boolean. Vòng lặp for-in gọi phương thức này trước để đảm bảo rằng vùng chứa không trống.
  • Lớp, giao diện hoặc bản ghi được trả về bởi GetEnumerator() phải chứa một phiên bản công khai, thuộc tính chỉ đọc có tên là Current. Loại thuộc tính Current phải là loại có trong bộ sưu tập .

Bộ đếm tùy chỉnh trả về từng giá trị từ bộ sưu tập qua thuộc tính Current. Và điều đó ngụ ý rằng giá trị được sao chép.

Vì vậy, điều này có nghĩa là các điều tra viên tùy chỉnh luôn sử dụng các bản sao.Hãy tưởng tượng nếu điều tra tích hợp cho mảng có thể sử dụng tài liệu tham khảo. Điều đó sẽ dẫn đến sự khác biệt đáng kể về ngữ nghĩa giữa hai loại điều tra viên. Chắc chắn rằng các nhà thiết kế đã chọn tính nhất quán giữa ngữ nghĩa của các loại khác nhau của điều tra viên

+0

Nếu một bản sao được thực hiện thì tại sao không thể sửa đổi bản sao? Ví dụ item.SomeField1: = 'Test' cho một lỗi biên dịch. –

+1

Đơn giản, định nghĩa ngôn ngữ quyết định rằng các biến vòng lặp không thể sửa đổi được. –

+1

Có thể chính xác vì đây là bản sao và các thay đổi đối với bản sao sẽ không được lưu trong mảng – VitaliyG

5

@David trả lời rằng điều tra viên là bản sao của hồ sơ.

Ông cũng nói rằng việc bao bọc một mảng động bên trong một bản ghi sẽ cho phép một điều tra viên tùy chỉnh.

Dưới đây là một ví dụ về làm điều đó:

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. 

Sửa

Chỉ cần được hoàn chỉnh và theo dõi các ý kiến, đây là một ví dụ chứa chung cho bất kỳ mảng năng động của kỷ lục, với một tùy chỉnh điều tra viên.

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 Một số nhận xét. Toàn bộ hồ sơ có thể được thực hiện chung chung và do đó có thể sử dụng cho bất kỳ loại nào. Tôi cũng nói rằng việc sử dụng Create cho một phương thức instance là khó hiểu. Trông quá giống một nhà xây dựng. Tôi muốn có một trong hai lớp chức năng gọi là mới mà trả về một điều tra viên mới, hoặc một phương pháp dụ tên là khởi tạo. –

+0

@DavidHeffernan, làm cho nó chung chung, điều tra viên sẽ trả về một loại 'PT =^T;', nhưng mục vòng lặp được khai báo là 'PSomeRecord'. Làm cách nào để tránh lỗi 'loại không tương thích'? –

+0

Khai báo kiểu trả về của 'GetCurrent' là' Con trỏ' giải quyết lỗi 'loại không tương thích'. Không chắc chắn nếu đó là giải pháp tốt nhất mặc dù. –

Các vấn đề liên quan