Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2020-12

Selecting Records with a variable

15 messages · 8 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

James Richters

The best way to explain what I'm hoping to do is by example... Say I have this record:

Type
Axis_Record = Record
   X,Y,Z,A,B,C    : Double;
End;
Var
AxisValue : Axis_Record;

Instead of this:

Procedure ShowAxis(Axisletter:Char);
Begin
   If AxisLetter = 'X' Then
      Writeln(AxisValue.X);
   If AxisLetter = 'Y' Then
      Writeln(AxisValue.Y);
   If AxisLetter = 'Z' Then
      Writeln(AxisValue.Z);
   If AxisLetter = 'A' Then
      Writeln(AxisValue.A);
   If AxisLetter = 'B' Then
      Writeln(AxisValue.B);
   If AxisLetter = 'C' Then
      Writeln(AxisValue.C);
End;

I would rather have something like this:

Procedure ShowAxis(Axisletter:Char);
Begin
   Writeln(AxisValue.AxisLetter);
End;

Is there some syntax that would work to select the correct record based on the variable so I can avoid having all the If statements?

James

Unknown author
to James Richters
James Richters wrote
Is there some syntax that would work to select the correct record based on the variable so I can avoid having all the If statements?

do you need to show only one at a time or are you looping through and printing all of them each time?

if you need them all every time and if you are looping through them, don't... just call ShowAxis and writeln all of them one after the other...

Procedure ShowAxis;
begin
   writeln(AxisValue.X);
   writeln(AxisValue.Y);
   writeln(AxisValue.Z);
   writeln(AxisValue.A);
   writeln(AxisValue.B);
   writeln(AxisValue.C);
end;
NOTE: No off-list assistance is given without prior approval. *Please keep mailing list traffic on the list where it belongs!*
James Richters
to Unknown author

No, this is just a simplified example..
The procedure will be working with dozens of records, so I need a way to call the procedure and specify which element of all the records to use... it will not be called for every element.

James

Earlier message 29 lines
-----Original Message----- From: fpc-pascal On Behalf Of wkitty42--- via fpc-pascal Sent: Saturday, December 19, 2020 8:26 PM To: Cc: Subject: Re: [fpc-pascal] Selecting Records with a variable On 12/19/20 7:16 PM, James Richters via fpc-pascal wrote: > Is there some syntax that would work to select the correct record based on the variable so I can avoid having all the If statements? do you need to show only one at a time or are you looping through and printing all of them each time? if you need them all every time and if you are looping through them, don't... just call ShowAxis and writeln all of them one after the other... Procedure ShowAxis; begin writeln(AxisValue.X); writeln(AxisValue.Y); writeln(AxisValue.Z); writeln(AxisValue.A); writeln(AxisValue.B); writeln(AxisValue.C); end; -- NOTE: No off-list assistance is given without prior approval. *Please keep mailing list traffic on the list where it belongs!* _______________________________________________
Jean SUZINEAU
to James Richters: The best way to explain what I'm hoping to do is by example... Say I have this record:…

I know you don't like objects, but maybe something like :

====================================================

unit uAxisRecord;
{$mode objfpc}{$H+}
interface
uses
  Classes, SysUtils;
type

 { TAxisRecord }

  TAxisRecord
  =
   object
     X,Y,Z,A,B,C: Double;
     function Value_from_Letter( _Axisletter:Char): double;
     procedure ShowAxis( _Axisletter:Char);
   end;
implementation

{ TAxisRecord }

function TAxisRecord.Value_from_Letter(_Axisletter: Char): double;
begin
      case _Axisletter
      of
        'X': Result:= X;
        'Y': Result:= Y;
        'Z': Result:= Z;
        'A': Result:= A;
        'B': Result:= B;
        'C': Result:= C;
        else Result:= X;//or throw an exception
        end;
end;
procedure TAxisRecord.ShowAxis(_Axisletter: Char);
begin
      WriteLn( Value_from_Letter( _Axisletter));
end;
end.

==========================================

And then where you need it:

...
uses uAxisRecord
...

Var
   AxisValue : TAxisRecord;

...

Procedure ShowAxis(Axisletter:Char);
Begin
    Writeln(AxisValue.Value_from_Letter( Axisletter);
End;

or just:

AxisValue.ShowAxis('X');

"object" works as "record", no need to allocate or call a constructor, but you can define methods on it.

(I didn't test but it should work)

Sven Barth
to Jean SUZINEAU
Jean SUZINEAU wrote
I know you don't like objects, but maybe something like :

Using modeswitch AdvancedRecords that also works with record types.

And I'd suggest a case-statement as well. Anything else will only be slower or just as fragile.

Regards,
Sven

Tomas Hajny
to Sven Barth
Sven Barth wrote 9 lines
Am 20.12.2020 um 05:26 schrieb Jean SUZINEAU via fpc-pascal: > > I know you don't like objects, but maybe something like : > Using modeswitch AdvancedRecords that also works with record types. And I'd suggest a case-statement as well. Anything else will only be slower or just as fragile.

If I understand the original case correctly, the wanted behaviour should be doable using (extended) RTTI, shouldn't it? Not that I'd know how to do that myself, but my (admittedly limited) understanding of RTTI suggests this as a possible solution, am I wrong?

Tomas

Unknown author
to James Richters: No, this is just a simplified example.. The procedure will be working with dozens of…
James Richters wrote
No, this is just a simplified example..

ok...

The procedure will be working with dozens of records, so I need a way to call the procedure and specify which element of all the records to use... it will not be called for every element.

in that case, the object (aka advanced records) form that Jean SUZINEAU presented would be what i would go for... they're great for things like this... i really enjoyed using them for BBS local message base storage formats years back... it made things so much easier...

NOTE: No off-list assistance is given without prior approval. *Please keep mailing list traffic on the list where it belongs!*
Sven Barth
to Tomas Hajny: If I understand the original case correctly, the wanted behaviour should be doable using…
Tomas Hajny wrote 15 lines
On 2020-12-20 11:56, Sven Barth via fpc-pascal wrote: > Am 20.12.2020 um 05:26 schrieb Jean SUZINEAU via fpc-pascal: >> >> I know you don't like objects, but maybe something like : >> > > Using modeswitch AdvancedRecords that also works with record types. > > And I'd suggest a case-statement as well. Anything else will only be > slower or just as fragile. If I understand the original case correctly, the wanted behaviour should be doable using (extended) RTTI, shouldn't it? Not that I'd know how to do that myself, but my (admittedly limited) understanding of RTTI suggests this as a possible solution, am I wrong?

That's why I said "slower or just as fragile". The solution with the (not yet existing) extended RTTI would work, but it would be slower than the hardcoded case statement.

Regards,
Sven

James Richters
to Unknown author: ok... in that case, the object (aka advanced records) form that Jean SUZINEAU presented…

What I’m hopping to accomplish isn't to get it to work... it's to consolidate a whole bunch of functions and procedures that are all exactly the same except for which axis I am working with...

I have 9 possible Axis, X,Y,Z,W,L.R,A,B,C

And I have Procedures:

Procedure Move_X;
Begin
  DoSomething(Variable1.X);
  DoAThing2;
  DoAThing3;
...
   Do more things that are the same all the time
...
  DoSomethingElse(Variable2.X);
  Variable3.X := SomeFormula(Variable4.X * ScaleFactor.X);
...
70 lines of things what work only with the .X variable
...
End;
Procedure Move_Y;
Begin
  DoSomething(Variable1.Y);
  DoAThing2;
  DoAThing3;
...
   Do more things that are the same all the time
...
  DoSomethingElse(Variable2.Y);
  Variable3.Y := SomeFormula(Variable4.Y * ScaleFactor.Y);
...
70 lines of things what work only with the .Y variable
...
End;

...
Same thing 7 more times
...

This is a tedious situation. If I want to change something I have to copy and paste it 9 times then go fix all the variables 9 times.. and it's easy to make a mistake.

What I want is just one uncomplicated procedure:

Procedure Move_It(Axis_Letter:Char);
Begin
  DoSomething(Variable1. Axis_Letter);
  DoAThing2;
  DoAThing3;
...
   Do more things that are the same all the time
...
  DoSomethingElse(Variable2. Axis_Letter);
  Variable3. Axis_Letter := SomeFormula(Variable4. Axis_Letter * ScaleFactor. Axis_Letter);
...
70 lines of things what work only with the . Axis_Letter variable
...
End;
If I need a bunch of case statements, it's atcually worse than if I just have separate procedures.
Procedure Move_It(Axis_Letter:Char);
Begin
Case Axis_Letter of
X:
   Begin
      DoSomething(Variable1. X);
    End;
Y:
   Begin
      DoSomething(Variable1. Y);
    End;
...
7 more times
...
End;
  DoAThing2;
  DoAThing3;
...
   Do more things that are the same all the time
...
Case Axis_Letter of
X:
   Begin
     DoSomethingElse(Variable2. X);
     Variable3. X := SomeFormula(Variable4.X * ScaleFactor. X);
   End;
Y:
   Begin
     DoSomethingElse(Variable2. Y);
     Variable3. Y := SomeFormula(Variable4.Y * ScaleFactor.Y);
   End;
...
7 more times
...
...
70 lines of things that all have a 9 case statements, sometimes I can combine them, most of the time not.
...
End;

James

Luca Olivetti
to James Richters

El 20/12/20 a les 16:02, James Richters via fpc-pascal ha escrit:

What I’m hopping to accomplish isn't to get it to work... it's to consolidate a whole bunch of functions and procedures that are all exactly the same except for which axis I am working with... I have 9 possible Axis, X,Y,Z,W,L.R,A,B,C

....

Show quoted text 6 lines
... Same thing 7 more times ... This is a tedious situation. If I want to change something I have to copy and paste it 9 times then go fix all the variables 9 times.. and it's easy to make a mistake.

Then change your data model.

Instead of

Axis_record = record
   X,Y,Z,A,B,C    : Double;
End;

use

AxisName = (X,Y,Z,A,B,C);
Axis_record = array[AxisName] of double;

then it's easy to do what you want.

procedure DoSomething(var Axis:Axis_record; const which:AxisName);
begin
   Axis[which]:=Axis[which]/2;
end;

Bye

Luca
James Richters
to Luca Olivetti

Thank you Luca, that will work much better for me!

James

Earlier message 44 lines
-----Original Message----- From: fpc-pascal On Behalf Of Luca Olivetti via fpc-pascal Sent: Sunday, December 20, 2020 10:11 AM To: Cc: Luca Olivetti Subject: Re: [fpc-pascal] Selecting Records with a variable El 20/12/20 a les 16:02, James Richters via fpc-pascal ha escrit: > What I’m hopping to accomplish isn't to get it to work... it's to > consolidate a whole bunch of functions and procedures that are all exactly the same except for which axis I am working with... > > I have 9 possible Axis, X,Y,Z,W,L.R,A,B,C .... > ... > Same thing 7 more times > ... > > > This is a tedious situation. If I want to change something I have to copy and paste it 9 times then go fix all the variables 9 times.. and it's easy to make a mistake. Then change your data model. Instead of Axis_record = record X,Y,Z,A,B,C : Double; End; use AxisName = (X,Y,Z,A,B,C); Axis_record = array[AxisName] of double; then it's easy to do what you want. procedure DoSomething(var Axis:Axis_record; const which:AxisName); begin Axis[which]:=Axis[which]/2; end; Bye -- Luca
Ralf Quint
to Luca Olivetti: El 20/12/20 a les 16:02, James Richters via fpc-pascal ha escrit: .... Then change your…
Luca Olivetti wrote 22 lines
Then change your data model. Instead of Axis_record = record   X,Y,Z,A,B,C    : Double; End; use AxisName = (X,Y,Z,A,B,C); Axis_record = array[AxisName] of double; then it's easy to do what you want. procedure DoSomething(var Axis:Axis_record; const which:AxisName); begin   Axis[which]:=Axis[which]/2; end; Bye

+1

I think this is a typical example where properly thinking about a solution for a programming issue BEFORE going with what seems as the logical choice at first glance leads to a much simpler solution without coming up with all kinds of fancy workarounds...

Ralf

This email has been checked for viruses by Avast antivirus software. https://www.avast.com/antivirus
James Richters
to Ralf Quint

Yes, I was trying to keep my data structure, because there were thousands of occurrences .. This project has had that structure since Turbo Pascal back in the 80s when it only had 3 axis It seemed like a good way to have variables all with the same name, and back then it wasn't so bad to have repeated code, but now I have 9 axis and it's getting ridiculous.. once I realized that Arrays would allow me to simplify things greatly, it turned out to not be so bad to change it... just a bunch of global search and replaces and a little fixing here and there that actually made it cleaner, and the whole project has been changed over in a relatively short amount of time... and now I can start consolidating all those functions and procedures. It was way less work to just change it all to a better structure than it was to work around it.

James

I think this is a typical example where properly thinking about a solution for a programming issue BEFORE going with what seems as the logical choice at first glance leads to a much simpler solution without coming up with all kinds of fancy workarounds...
Stefan V. Pantazi
to James Richters

James, I remember going through some similar motions as you are. Looks like you are searching for a form of indexing your record fields. So many good suggestions, I had to join the fray and try something... My preferred approach would also be an ordinal type for the axes and a record variant, as already suggested. Objects and advanced records would be fine too. That way you get to keep both worlds while evolving your software to a more elegant approach along the lines of some of the suggested solutions.
Here is something I tried on top of your example to make sure it actually works.

program example1;
Type
Axis_Name=(anX,anY,anZ,anA,anB,anC);
Axis_Array=array[Axis_Name] of Double;
Axis_Record = Record
   X,Y,Z,A,B,C: Double;
End;
Axis_Record_variant = Record
   case boolean of
    false: (AxRec:Axis_Record);
    true: (AxArr:Axis_Array);
End;
Var
AxisValueVar : Axis_Record_variant;
AxisValue : Axis_Record absolute AxisValueVar.AxRec;
//Instead of this:
Procedure ShowAxis(Axisletter:Char);
Begin
   If AxisLetter = 'X' Then
      Writeln(AxisValue.X);
   If AxisLetter = 'Y' Then
      Writeln(AxisValue.Y);
   If AxisLetter = 'Z' Then
      Writeln(AxisValue.Z);
   If AxisLetter = 'A' Then
      Writeln(AxisValue.A);
   If AxisLetter = 'B' Then
      Writeln(AxisValue.B);
   If AxisLetter = 'C' Then
      Writeln(AxisValue.C);
End;
//I would rather have something like this:
Procedure ShowAxisVariant(AxisName:Axis_Name);
Begin
   Writeln(AxisValueVar.AxArr[AxisName]);
End;
begin
  AxisValue.Z := 1234.5678;
  ShowAxis('Z');
 ShowAxisVariant(anZ);
end.

On Sun, Dec 20, 2020 at 2:01 PM James Richters via fpc-pascal <

Show quoted text 30 lines
wrote: Yes, I was trying to keep my data structure, because there were thousands of occurrences .. This project has had that structure since Turbo Pascal back in the 80s when it only had 3 axis It seemed like a good way to have variables all with the same name, and back then it wasn't so bad to have repeated code, but now I have 9 axis and it's getting ridiculous.. once I realized that Arrays would allow me to simplify things greatly, it turned out to not be so bad to change it... just a bunch of global search and replaces and a little fixing here and there that actually made it cleaner, and the whole project has been changed over in a relatively short amount of time... and now I can start consolidating all those functions and procedures. It was way less work to just change it all to a better structure than it was to work around it. James >I think this is a typical example where properly thinking about a solution for a programming issue BEFORE going with >what seems as the logical choice at first glance leads to a much simpler solution without coming up with all kinds of fancy workarounds... _______________________________________________ fpc-pascal maillist - https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Jean SUZINEAU
to James Richters: What I’m hopping to accomplish isn't to get it to work... it's to consolidate a whole…

Le 20/12/2020 à 16:02, James Richters via fpc-pascal a écrit :

If I need a bunch of case statements, it's atcually worse than if I just have separate procedures.

No, in fact the case statement is written only once, in the implementation of TAxisRecord, and in procedure
Move_It(Axis_Letter:Char); , you just use:

DoSomething(Variable1.Value_from_Letter(Axis_Letter));

This said, I think that Lucas and Stefan ideas with "AxisName = (X,Y,Z,A,B,C);" are better than mine,

because compilation will fail on a bad axis name, for example a typo with Axis[U].