for-in with an array of record

for-in with an array of record

why the for-in statement calls copyRecord instead of a simple refence to the item ?!

and then, why it is read-only ?! 

it make it useless I think !

with a reference to the record, it could be a nice replacement for that:

for Index := 0 to Length(arrayOfRec) - 1 do
  with arrayOfRec[Index] do  
  begin
    field := Value;
  end;

but this code do not works !

for rec in arrayOfRec do
begin
  rec.field := Value: // readonly ! and rec is a copy of arrayOfRec[x]
end;

Comments

  1. What is arrayOfRec? Does it have reference types in it? Full example please (;

    ReplyDelete
  2. type
      TRec = record
        s: string;
        i : Integer;
      end;
    var
      a: TArray;
      r: TRec;
    begin
      SetLength(a, 1);
      for r in a do
      begin
        r.s := 'hello'; // readonly !
      end;
    end;

    ReplyDelete
  3. If it was a reference it would be inconsistent with other enumerators, implemented via GetEnumerator, as Delphi does not support reference types.

    Of course, the compiler could have optimized the code to use a reference under the hood, if it was clever enough. But this should be transparent to the user.

    ReplyDelete
  4. inconsistent...but usefull anyway :D

    ReplyDelete
  5. Paul TOTH Well I can see what you mean, but you shouldn't really do that in a regular for loop anyway.

    ReplyDelete
  6. Asbjørn Heid guess why ?!  they're many cases where I need to initialize an array of records, and usually I also need the index BTW...juste like foreach($array as $index => &$value) in PHP

    hum...there's a way  with a classic GetEnumerator !

    type
      TRecord = record
        s: string;
      end;

      TEnumOf = record
      type
        PT = ^T;
      private
        FItems: TArray;
        FIndex: Integer;
        function GetCurrent: PT; inline;
      public
        constructor Create(Items: TArray);
        function MoveNext: Boolean;
        property Current: PT read GetCurrent;
      end;

      TArrayOf = record
        Items: TArray;
        function GetEnumerator: TEnumOf;
      end;

    procedure TForm1.FormCreate(Sender: TObject);
    var
      a: TArrayOf;
      p: ^TRecord;
      r: TRecord;
    begin
      SetLength(a.Items, 1);
      for p in a do
      begin
        p.s := 'Hello';
      end;
      for r in a.Items do
      begin
        ShowMessage(r.s);
      end;
    end;

    { TEnumOf }

    constructor TEnumOf.Create(Items: TArray);
    begin
      FItems := Items;
      FIndex := -1;
    end;

    function TEnumOf.GetCurrent: PT;
    begin
      Result :=@FItems[FIndex];
    end;

    function TEnumOf.MoveNext: Boolean;
    begin
      Result := FIndex < Length(FItems) - 1;
      if Result then
        Inc(FIndex);
    end;

    { TArrayOf }

    function TArrayOf.GetEnumerator: TEnumOf;
    begin
      Result.Create(Items);
    end;

    ReplyDelete
  7. and perhaps a better way to declare TArrayOf

      TArrayOf = record
      type
        PT = ^T;
      private
        FItems: TArray;
        function GetItem(Index: Integer): PT; inline;
        function GetCount: Integer; inline;
        procedure SetCount(const Value: Integer); inline;
      public
        function GetEnumerator: TEnumOf;
        property Count: Integer read GetCount write SetCount;
        property Items[Index: Integer]: PT read GetItem; default;
      end;

    ReplyDelete
  8. If you're initializing a structured entity like a record or class, that should be done with a constructor.

    ReplyDelete
  9. We need to be able to enumerate references as well as values, as can be done in good languages. I'm looking at you D. So one might imagine writing:

        for var rec in arrayOfRec do
          foo(rec);

    ReplyDelete
  10. I don't see any case where you want a copy of the record

    ReplyDelete
  11. Paul TOTH that's what you get form a function on a non-reference type: a copy. For a reference type you get a copy of the reference. Since for ... in are function based, you get copies.

    ReplyDelete
  12. an other option (with  TEnumOf above)

      TArrayOf = record
        Items: TArray;
        function GetEnumerator: TEnumOf;
      end;

      TIterator = class
        class function each(Items: TArray): TArrayOf;
      end;

    class function TIterator.each(Items: TArray): TArrayOf;
    begin
      Result.Items := Items;
    end;

    var
      a: TArray;
      p: ^TRecord;
    begin
      for p in TIterator.each(b) do
      begin
       p.s := 'Hello';
      end;
    end;

    don't know why the type inference don't work 

    any way, many things to do for something that should be easier to do !

    ReplyDelete
  13. Jeroen Wiert Pluimers That's a flawed view of the system. Theres' no reason why a function cannot return a reference. They frequently do. Any function that returns something derived from TObject is returning a reference.

    Other languages manage to implement iterators that yield references rather than copies. Ask yourself how they managed that.

    ReplyDelete
  14. David Heffernan In C++ terms that's a pointer and not a reference. Delphi does not have "proper" reference types, as you know, and I agree that it should have. Not sure about syntax though...

    ReplyDelete
  15. Asbjørn Heid Yes, I'm inventing Delphi references here. With one possible syntax. 

    Support for references would make a lot of things easier and cleaner. Whether or not the benefits outweigh the costs (for Emba) I am less sure.

    ReplyDelete
  16. AFAIK there's no rule about what GetEnumerator should return, so a pointer to the record is valid and is a reference.

    and a reference can be usefull in many cases, not only for records

    var
      t: TArray;
      i: Integer;
    begin
      SetLength(t, 10);
      for i in t do
        i := random(50);
    // could be a array of 10 random values
    end:

    var
      t: TArray;
      b: Integer;
    begin
      SetLength(t, 10);
      for b in t do 
        b := TButton.Create(Self);
    end:

    ReplyDelete
  17. David Heffernan Yeah I've missed them a lot coming back to Delphi from C++. Not just easier and cleaner, but also a lot more efficient in many common cases.

    ReplyDelete
  18. Paul TOTH Yes, it's easy enough to write enumerators that return pointers to values. But language support for references would make life much cleaner.

    ReplyDelete

Post a Comment