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.
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 value100
to theSize
property of the object referenced bySomeObject
; you would not write this asSomeObject^.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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With