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;
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;
What is arrayOfRec? Does it have reference types in it? Full example please (;
ReplyDeletetype
ReplyDeleteTRec = 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;
If it was a reference it would be inconsistent with other enumerators, implemented via GetEnumerator, as Delphi does not support reference types.
ReplyDeleteOf 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.
inconsistent...but usefull anyway :D
ReplyDeletePaul TOTH Well I can see what you mean, but you shouldn't really do that in a regular for loop anyway.
ReplyDeleteAsbjø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
ReplyDeletehum...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;
and perhaps a better way to declare TArrayOf
ReplyDeleteTArrayOf = 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;
If you're initializing a structured entity like a record or class, that should be done with a constructor.
ReplyDeleteWe 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:
ReplyDeletefor var rec in arrayOfRec do
foo(rec);
I don't see any case where you want a copy of the record
ReplyDeletePaul 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.
ReplyDeletean other option (with TEnumOf above)
ReplyDeleteTArrayOf = 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 !
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.
ReplyDeleteOther languages manage to implement iterators that yield references rather than copies. Ask yourself how they managed that.
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...
ReplyDeleteAsbjørn Heid Yes, I'm inventing Delphi references here. With one possible syntax.
ReplyDeleteSupport 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.
AFAIK there's no rule about what GetEnumerator should return, so a pointer to the record is valid and is a reference.
ReplyDeleteand 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:
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.
ReplyDeletePaul 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