【问题标题】:How to implement a custom setlength()如何实现自定义 setlength()
【发布时间】:2016-06-21 15:17:11
【问题描述】:

我想实现我自己的 MySetLength() 函数,它可能完全作为数组的原始 SetLength()(来自 delphi 6)工作,除了我想将 float 参数作为维度传递(然后,最终,在我的 func 中将其舍入,然后再调用原始 Setlength())。例如:

procedure MySetLength(var A: Array type; Len1: **double**; Len2...);

代替原来的:

procedure SetLength(var A: Array type; Len1: **Integer**; Len2...);

其中 A 可以有任意数量的维度并且可以是任意类型(字符串或浮点数)

【问题讨论】:

  • 那么,您的问题/问题是什么?
  • SetLength 很神奇,无法精确复制。虽然问题是可行的,但您需要允许一些限制。
  • @danmatei:你是在 Delphi 6 中编写的,还是只是希望它像在 Delphi 6 中那样运行?如果您使用的是更高版本,则可以使用泛型和重载来处理此问题。但是,如果您实际使用的是 Delphi 6,则不能选择泛型。
  • @RemyLebeau,重载不适用于SetLength/Length。由于它们是内在的,重载将隐藏内在函数。不久前,我们与新引入的Low(string)\High(String) 一起讨论过这个问题。
  • @LURD:我建议MySetLength() 本身可以重载,一个版本用于String 输入,一个版本用于数组输入。 procedure MySetLength(var A: String; Len1: Double); overload; type TWhateverArray = array of Whatever; procedure MySetLength(var A: TWhateverArray; Len1: Double); overload; procedure MySetLength(var A: TWhateverArray; Len1, Len2: Double); overload;等...

标签: delphi delphi-6


【解决方案1】:

实现它并使其完全像SetLength 那样工作是不可能的,因为SetLength 是一个具有编译器和运行时支持的内在函数。这就是函数能够接受任意数组和字符串类型以及接受任意数量的维度参数的方式。只有编译器供应商才能制作这样的功能。

您真的没有希望生产任何符合问题规范的远程有用的东西。毫无疑问,最好的方法是直接调用SetLength 并将Round 函数应用于任何需要四舍五入的参数。

【讨论】:

  • 我已经对每个参数进行了四舍五入,但我认为有一个更简单的解决方案,因为编辑大量代码既乏味又丑陋,在所有地方添加 ROUND()。谢谢。
  • 也许您可以通过封装这些数组来减少这种影响,这样您就不会在应用程序代码中直接引用它们,而只能在库代码中引用它们。
  • @danmatei - Round() 使用称为“银行家四舍五入”的特定舍入策略,其中 0.5 舍入到最接近的偶数(可能向上向下)。这就是为什么我建议您实施您确定适合您的数组尺寸的特定舍入策略并将其应用到您的 SetLength() 调用中。我将更新我的答案,以便让那些还不知道的人清楚。
【解决方案2】:

想到重载和泛型:

type
  TMyHelper = class
  public
    class procedure SetLength(var A: String; Len: Double); overload; static;
    class procedure SetLength<T>(var A: TArray<T>; Len: Double); overload; static;
    class procedure SetLength<T>(var A: TArray<TArray<T>>; Len1, Len2: Double); overload; static;
    // as so on...
  end;

class procedure TMyHelper.SetLength(var A: String; Len: Double);
var
  iLen: Integer;
begin
  iLen := ...; // round Len as needed...
  System.SetLength(A, iLen);
end;

class procedure TMyHelper.SetLength<T>(var A: TArray<T>; Len: Double);
var
  iLen: Integer;
begin
  iLen := ...; // round Len as needed...
  System.SetLength(A, iLen);
end;

class procedure TMyHelper.SetLength<T>(var A: TArray<TArray<T>>; Len1, Len2: Double);
var
  iLen1, iLen2: Integer;
begin
  iLen1 := ...; // round Len1 as needed...
  iLen2 := ...; // round Len2 as needed...
  System.SetLength(A, iLen1, iLen2);
end;

// and so on...

var
  s: string;
  a1: TArray<Integer>;
  a2: TArray<TArray<String>>;
begin
  TMyHelper.SetLength(s, 10);
  TMyHelper.SetLength<Integer>(a1, 10);
  TMyHelper.SetLength<String>(a2, 5, 10);
end;

如果你使用的 Delphi 版本不支持泛型,你一般不能重载数组元素类型,但如果你使用的元素类型有限,你仍然可以重载数组,例如:

type
  TInteger1DimArray = array of Integer;
  TInteger2DimArray = array of TInteger1DimArray;

  TString1DimArray = array of String;
  TString2DimArray = array of TString1DimArray;

  // and so on...

  TMyHelper = class
  public
    class procedure SetLength(var A: String; Len: Double); overload; static;
    class procedure SetLength(var A: TInteger1DimArray; Len: Double); overload; static;
    class procedure SetLength(var A: TInteger2DimArray; Len1, Len2: Double); overload; static;
    class procedure SetLength(var A: TString1DimArray; Len: Double); overload; static;
    class procedure SetLength(var A: TString2DimArray; Len1, Len2: Double); overload; static;
    // as so on...
  end;

class procedure TMyHelper.SetLength(var A: String; Len: Double);
var
  iLen: Integer;
begin
  iLen := ...; // round Len as needed...
  System.SetLength(A, iLen);
end;

class procedure TMyHelper.SetLength(var A: TInteger1DimArray; Len: Double);
var
  iLen: Integer;
begin
  iLen := ...; // round Len as needed...
  System.SetLength(A, iLen);
end;

class procedure TMyHelper.SetLength(var A: TInteger2DimArray; Len1, Len2: Double);
var
  iLen1, iLen2: Integer;
begin
  iLen1 := ...; // round Len1 as needed...
  iLen2 := ...; // round Len2 as needed...
  System.SetLength(A, iLen1, iLen2);
end;

class procedure TMyHelper.SetLength(var A: TString1DimArray; Len: Double);
var
  iLen: Integer;
begin
  iLen := ...; // round Len as needed...
  System.SetLength(A, iLen);
end;

class procedure TMyHelper.SetLength(var A: TString2DimArray; Len1, Len2: Double);
var
  iLen1, iLen2: Integer;
begin
  iLen1 := ...; // round Len1 as needed...
  iLen2 := ...; // round Len2 as needed...
  System.SetLength(A, iLen1, iLen2);
end;

// and so on...

var
  s: string;
  a1: TInteger1DimArray;
  a2: TString2DimArray;
begin
  TMyHelper.SetLength(s, 10);
  TMyHelper.SetLength(a1, 10);
  TMyHelper.SetLength(a2, 5, 10);
end;

如您所见,为单个String 实现SetLength() 很容易,但为数组实现SetLength() 可能会变得相当复杂,具体取决于您使用的数组类型。此解决方案与您可以在不借助 RTTI 的情况下获取用户代码一样接近,因此您可以直接调用 RTL 的 DynArraySetLength() 函数。

【讨论】:

  • 谢谢,但您的解决方案不涵盖任意数量的数组维度。
  • @danmatei 的效率不如原生的SetLength(),但可以根据需要为每个额外的维度定义额外的重载。
【解决方案3】:

为什么要解决重载或替换内在函数的问题,让事情变得复杂?

为什么不实施您将在替换 SetLength 中使用的舍入策略,并在适当的情况下将其简单地合并到对内部函数的调用中?

SetLength(arr, ArrayDim(dbl));

当然,您保留了 SetLength 的所有功能,包括在必要时将双精度值的维度边界规则应用于多个维度的能力:

SetLength(multiDimArr, ArrayDim(dblDim1), ArrayDim(dblDim2));

ArrayDim(或您认为合适的名称)是一个实现舍入策略并返回指定输入(双)。

您为什么要这样做而不是简单地使用 Round()

答案是 Delphi 中的 Round() 使用一种称为 Bankers Rounding 的特殊舍入策略。这意味着 n.5 可以向上向下取整,具体取决于 n 的值。

即使这种舍入策略目前适合您的需求,您也应该实现一个函数来显式应用适合数组维度的特定舍入策略,并在 SetLength() 调用中使用它。 p>

这不仅可以确保您在每种情况下都应用正确的舍入策略,还意味着如果您需要或决定更改数组维度的舍入策略,您只需更改函数 ,而不是所有对 SetLength() 使用该函数的调用。

【讨论】:

  • 如果 ARR 可以有任意数量的维度怎么办?
  • SetLength 接受任意维度的数组
  • var arr: array of array of ...; ... SetLength(arr, ArrayDim(dbl1), ArrayDim (dbl2), ...);
猜你喜欢
  • 1970-01-01
  • 2010-12-02
  • 2016-07-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-18
  • 2010-11-17
相关资源
最近更新 更多