【问题标题】:Efficiently compare an integer against a static list of integers in Delphi?有效地将整数与 Delphi 中的静态整数列表进行比较?
【发布时间】:2015-07-24 14:37:07
【问题描述】:

any way to compare an integer variable to a list of integers in if statement 类似,但在 Delphi 中,我如何测试整数变量是否包含在整数的静态“列表”(松散地说 - 一个预定义的集合)中(可能是 consts 或 显式值列表)在 if 语句中?

现在我会做这样的事情:

if (x = 1) or
   (x = 2263) or
   (x = 263553) or
   (x = whatever_int) then
begin
  //do something;
end;

但我试图避免条件的冗长序列。

我正在使用 Delphi XE6,所以如果有什么新的东西可以以干净的方式实现这一点,请告诉我。

【问题讨论】:

  • 您目前如何存储这个整数列表?清单有多大?您是否关心运行时效率而不是代码的清晰度?
  • 由于您要求比较发生在if 语句中(甚至标记为粗体),我不敢建议使用case 语句。
  • @TomBrunberg 是否可行取决于 whatever_int 是否为 const。
  • 不幸的是,我无法发布作为答案,但如果正如您所说,您检查的所有值在编译时都是已知的,您可以使用 case 语句:case x of /n 1, 2263, 263553, declared_const, other_declared_const, 1048000:/n // 做某事/n else // 可选做其他事情/n end;/n,,, 字符 /n 表示正常帕斯卡格式的换行
  • @TomBrunberg,如果您愿意,可以发表您的评论作为答案吗?我会赞成的。谢谢。

标签: delphi delphi-xe6


【解决方案1】:

您目前的方法很好。编译器将产生高效的代码。如果效率是最重要的,那么您可以选择比较的顺序,以便首先检查最常见的值。这需要了解输入数据的分布情况。

另一种语法方法是case 语句。例如:

case x of
1, 2263, 263553, whatever_int:
  // do other stuff
end;

这会产生与if 语句相似(实际上我怀疑相同)的代码。但它可能更简洁,更易于阅读。在某些情况下,编译器能够生成跳转表。

如果效率对您来说至关重要,那么请务必检查编译器发出的代码、发布编译器选项并执行分析。

我确实想知道您所说的高效是什么意思。在编程中,该术语具有与运行时性能相关的特定含义。但我想知道您是否真的更关心编写清晰简洁的代码,在这种情况下,高效是错误的术语。您的问题谈到避免重复 or 操作员这一事实让我怀疑效率对您来说很重要。

另一方面,如果运行时性能很重要,那么考虑放弃代码清晰度并实现索引跳转表。但是,在没有首先确定它是一个瓶颈并分析您所做的任何更改之前,永远不要优化代码。

【讨论】:

  • case sometimes 甚至比订购了ifs...不要问我为什么,我还没有从那个答案中检查生成的代码。
  • @TLama 编译器有时会生成跳转表。我想。
  • 我通常更喜欢case 版本来进行这样的测试;在我做的检查中,生成的代码几乎是一模一样的,我认为代码的可读性和可维护性都有很大的提高。
  • 抱歉我的英语很差,实际上我所说的高效是指清晰易读。所以案例解决方案可能就是我正在寻找的。​​span>
【解决方案2】:

您可以通过编写一个小函数使代码更清晰易读:

//NOTE: This function is written in a way that would be compatible with any version of Delphi.
//Since you're using XE6, there are some improvement options available to you:
// E.g. You can make the function inline, use "for in", use a record helper, use generics
function ContainsInteger(const AValue: Integer; const AArray: array of Integer): Boolean;
var
  I: Integer;
begin
  Result := False;
  for I := Low(AArray) to High(AArray) do
  begin
    if (AArray[I] = AValue) then
    begin
      Result := True;
      Exit;
    end;
  end;
end;

现在您可以按如下方式调用您的函数:

if ContainsInteger(x, [1, 2263, 263553, whatever_int]) then
begin
  //do something;
end;

注意:这会比大卫回答中描述的 case 语句要慢,但对于较长的数字列表,它有可能显着加快。

如果此函数可能会经常用于较长的列表:

  • 您可以指定输入数组按升序给出的要求。 (否则行为将是未定义的。)
  • 并修改函数以执行二分查找。
  • 例如,一个包含 32 个数字的列表最多需要 5 次比较才能使用二分搜索找到匹配项;而 case 语句平均需要 16 个。

注意:像往常一样微调优化:

  1. 在此处使用分析来确认您是否需要它。
  2. 使用分析来确认您是否获得了预期的好处。

【讨论】:

    【解决方案3】:

    考虑到用例,我会使用TList<Integer>,以及它的方法:

    • procedure AddRange(const Values: array of T);
    • Contains(const Value: T): Boolean;

    【讨论】:

      【解决方案4】:

      为什么不简单:

      x : integer;
      begin
        if x in [1,2,5] then
        begin
          showmessage('x in 1,2,5');
        end;
      end;
      

      【讨论】:

      • in 运算符只能用于从 0 到 255 的元素。问题以大于该值的值为例。
      • 请解释它是如何工作的以及它为什么有用。由于有人评论了他们认为您的解决方案的缺点,您可能希望显示结果使其可行。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-12
      • 2021-10-26
      • 1970-01-01
      • 2017-06-21
      • 1970-01-01
      • 2023-04-02
      相关资源
      最近更新 更多