Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I make a procedure change the value of a variable argument?

I tried to make a simple procedure that would remove spaces from an inputted string:

procedure RemoveSpaces(StringParameter : String);
var
   SpacePosition: Integer;
begin
   SpacePosition := Pos(' ',StringParameter);
   while SpacePos <> 0 do
   begin
      Delete(StringParameter,SpacePos,1);
      SpacePosition := Pos(' ',StringParameter);
   end;
end;

but after testing the procedure in the following code:

var
   Input : String;
begin
   Readln(Input);
   RemoveSpaces(Input);
   Writeln(Input);
   Readln;
end.

it was clear that the Input variable was simply regurgitated out of the procedure as if it was never changed.

I thought it might just be some limit to what I can do with procedures that I had just not yet realized but then I remembered that I had previously made a procedure that takes a StringList parameter and alphabetically sorts the Strings in the StringList and saves the changes to the StringList variable argument:

var
   myStringList : TStringList;

implementation

procedure StringListSort(StringList : TStringList);
begin
   //Sort StringList
end;

procedure TFormName.ButtonNameClick(Sender: TObject);
begin
   StringListSort(myStringList);
end; 

Any solution to my problem is welcomed.

like image 610
Justin Avatar asked Apr 20 '15 18:04

Justin


1 Answers

procedure RemoveSpaces(StringParameter: string);

You are passing the string parameter by value. Semantically1, a copy is made of the argument you pass, and any modifications made are made to that copy. The caller does not observe the modifications.

You need to pass the parameter by reference. When you pass by reference, the procedure operates on the original variable, and not a copy.

In Pascal, and Delphi, you pass by reference by using the var keyword. Change

procedure RemoveSpaces(StringParameter: string);

to

procedure RemoveSpaces(var StringParameter: string);

The reason that you observed what appeared to be different behaviour with a TStringList parameter is that TStringList is a class which is a reference type. That means that given List: TStringList then List is a reference to the object. For more discussion on the distinction between value and reference types see my answer here: Why should we use classes rather than records, or vice versa?

The documentation has this to say on the matter:

A variable of a class type is actually a pointer that references an object. Hence more than one variable can refer to the same object. Like other pointers, class-type variables can hold the value nil. But you don't have to explicitly dereference a class-type variable to access the object it points to. For example, SomeObject.Size := 100 assigns the value 100 to the Size property of the object referenced by SomeObject; you would not write this as SomeObject^.Size := 100.

Here the documentation is rather more explicit about the fact that class type variables are really just pointers.

You can implement your procedure more efficiently and simply by using the RTL function StringReplace:

procedure RemoveSpaces(var StringParameter: string);
begin
  StringParameter := StringReplace(StringParameter, ' ', '', [rfReplaceAll]);
end;

I would also comment that a function would probably be a better option here. That gives you the flexibility to pass something other than a variable, for instance a literal or a constant. And you can also compose and chain functions more easily.

So this would be

function SpacesRemoved(const str: string): string;
begin
  Result := StringReplace(str, ' ', '', [rfReplaceAll]);
end;

And then instead of having to write:

RemoveSpaces(Input);
Writeln(Input);

you can write

Writeln(SpacesRemoved(Input));

1 I said semantically because a string is a reference type, albeit a special one. The distinction is that if multiple variables refer to the same string object, then when a modification is made, the string is copied to a new object and the modifying variable is given a unique reference. This is known as copy-on-write and has the effect of making the string data type behave like a value type.

like image 84
5 revs Avatar answered Oct 03 '22 10:10

5 revs