如何使过程更改变量参数的值?

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

我尝试制作一个简单的程序来删除输入字符串中的空格:

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;

但在测试以下代码中的过程后:

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

很明显,输入变量只是从过程中反省出来,就好像它从未改变过一样。

我认为这可能只是我尚未意识到的过程对我能做的事情的一些限制,但后来我想起我以前制作了一个过程,该过程采用 StringList 参数并按字母顺序对列表中的字符串进行排序StringList 并将更改保存到 StringList 变量参数:

var
   myStringList : TStringList;

implementation

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

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

欢迎解决我的问题。

procedure RemoveSpaces(StringParameter: string);

您正在按值 传递字符串参数。在语义上1,一个副本是由你传递的参数组成的,并且对那个副本进行任何修改。调用者没有观察到修改。

您需要通过引用传递参数。当您通过引用传递时,该过程对原始变量而不是副本进行操作。

在 Pascal 中,以及 Delphi、you pass by reference by using the var keyword。变化

procedure RemoveSpaces(StringParameter: string);

procedure RemoveSpaces(var StringParameter: string);

您观察到使用 TStringList 参数时出现不同行为的原因是 TStringList 是一个 class,它是一个引用类型。这意味着给定 List: TStringList 然后 List 是对该对象的引用。有关值类型和引用类型之间区别的更多讨论,请在此处查看我的回答:Why should we use classes rather than records, or vice versa?

documentation 对此事有话要说:

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.

这里的文档更明确地说明 class 类型变量实际上只是指针。

您可以通过使用 RTL 函数更高效、更简单地实现您的程序 StringReplace:

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

我还要评论,函数可能是一个更好的选择。这使您可以灵活地传递变量以外的内容,例如文字或常量。您还可以更轻松地组合和链接函数。

所以这将是

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

然后不必写:

RemoveSpaces(Input);
Writeln(Input);

你可以写

Writeln(SpacesRemoved(Input));

1 我在语义上说是因为 string 是一个引用类型,尽管它是一个特殊的类型。不同之处在于,如果多个变量引用同一个字符串对象,则在进行修改时,会将字符串复制到一个新对象,并为修改变量提供唯一引用。这称为写时复制,具有使 string 数据类型表现得像值类型的效果。