【问题标题】:How to make a local array variable conditionally points to an open array parameter?如何使局部数组变量有条件地指向开放数组参数?
【发布时间】:2021-10-28 10:23:29
【问题描述】:

我正在尝试有条件地分配一个本地开放数组变量,以便它指向一个 const 开放数组参数:

uses
  System.Generics.Collections,
  System.Generics.Defaults;
    
type
  TArray = class(System.Generics.Collections.TArray)
  public
    class function  SameValues<T>(const AValuesA: array of T; AValuesB: array of T; const AComparer: IEqualityComparer<T>; AOrderMatters : Boolean = True) : Boolean; overload; static;
  end;
    
...
    
class function TArray.SameValues<T>(const AValuesA: array of T; AValuesB: array of T; const AComparer: IEqualityComparer<T>; AOrderMatters : Boolean = True) : Boolean;
var
  ArrA : TArray<T>;
  ArrB : TArray<T>;
  i : integer;
begin
  //checking sizes
  if(Length(AValuesA) <> Length(AValuesB)) then
  begin
    Result := False;
    Exit;
  end;

  if(AOrderMatters) then
  begin
    //I don't need to change the arrays, so I could directly point to the open array parameters
    ArrA := AValuesA;
    ArrB := AValuesB;
  end else
  begin
    //copying to local arrays
    SetLength(ArrA, Length(AValuesA));
    TArray.Copy<T>(AValuesA, ArrA, Length(AValuesA));
    SetLength(ArrB, Length(AValuesB));
    TArray.Copy<T>(AValuesB, ArrB, Length(AValuesB));

    //sorting local arrays
    TArray.Sort<T>(ArrA);
    TArray.Sort<T>(ArrB);
  end;

  //comparing elements
  i := 0;
  while(i < Length(ArrA)) do
  begin
    if(not AComparer.Equals(ArrA[i], ArrB[i])) then
    begin
      Result := False;
      Exit;
    end;
    Inc(i);
  end;
  Result := True;
end;

在编译时,它会在以下位置引发 E2010 错误:

//I don't need to change the arrays, so I could directly point to the open array parameters
ArrA := AValuesA;
ArrB := AValuesB;

E2010 不兼容的类型:“动态数组”和“T 数组”

我尝试了以下方法:

ArrA := @AValuesA;
ArrB := @AValuesB;

它会编译,但会在运行时引发 AV 异常(最重要的是,我不知道这是否是一种安全的方法)。

这是我的测试应用程序代码:

uses
  System.Generics.Defaults;

procedure TForm1.FormCreate(Sender: TObject);
var
  ArrA : array of string;
  ArrB : array of string;
begin
  SetLength(ArrA, 2);
  ArrA[0] := 'hello';
  ArrA[1] := 'world';

  SetLength(ArrB, 2);
  ArrB[0] := 'world';
  ArrB[1] := 'hello';

  if(TArray.SameValues<string>(ArrA, ArrB, TEqualityComparer<string>.Default, True)) then
    ShowMessage('Same values and same order')
  else if(TArray.SameValues<string>(ArrA, ArrB, TEqualityComparer<string>.Default, False)) then
    ShowMessage('Same values but different order')
  else
    ShowMessage('Different values');
end;

【问题讨论】:

  • 你不能这样做,这些类型只是不兼容。动态数组的元数据(即引用计数和长度)是对象本身的一部分,但对于开放数组,其元数据(只是长度)作为单独的参数传递给函数。
  • 哦!这太不舒服了!我会做两个分开的功能...谢谢
  • 您可以在一个函数中轻松完成。您只需创建副本,对其进行排序,然后再次递归调用相同的函数,但将True 传递给AOrderMatters。我会写一个答案来说明如何。
  • 是的,它肯定会是一个更简洁的代码,但我担心性能问题,如果不是绝对必要的话,我会尽量避免复制整个数组
  • 如果要排序,则必须复制数组,但如果不需要排序,则可以按照我的描述避免复制,正如我的回答所表明的那样。

标签: arrays delphi delphi-xe7


【解决方案1】:

您尝试做的事情是不可能的,因为开放数组和动态数组在您的代码尝试的方式上不兼容。

有一种简单的方法可以实现您想要的,使用简单的递归。像这样:

class function TArray.SameValues<T>(const AValuesA: array of T; AValuesB: array of T; const AComparer: IEqualityComparer<T>; AOrderMatters : Boolean = True) : Boolean;
var
  ArrA : TArray<T>;
  ArrB : TArray<T>;
  i : integer;
begin
  //checking sizes
  if Length(AValuesA) <> Length(AValuesB) then
  begin
    Result := False;
    Exit;
  end;

  if not AOrderMatters then
  begin
    //copying to local arrays
    SetLength(ArrA, Length(AValuesA));
    TArray.Copy<T>(AValuesA, ArrA, Length(AValuesA));
    SetLength(ArrB, Length(AValuesB));
    TArray.Copy<T>(AValuesB, ArrB, Length(AValuesB));

    //sorting local arrays
    TArray.Sort<T>(ArrA);
    TArray.Sort<T>(ArrB);

    Result := SameValues<T>(ArrA, ArrB, AComparer, True);
    Exit;
  end;

  //comparing elements
  for i := 0 to High(AValuesA) do
  begin
    if not AComparer.Equals(AValuesA[i], AValuesB[i]) then
    begin
      Result := False;
      Exit;
    end;
  end;

  Result := True;
end;

【讨论】:

    猜你喜欢
    • 2020-08-21
    • 2023-04-01
    • 2022-01-12
    • 2014-11-24
    • 1970-01-01
    • 1970-01-01
    • 2020-08-08
    • 1970-01-01
    • 2018-07-06
    相关资源
    最近更新 更多