子范围和运算符重载



我可以声明以下重载来扩展集合的限制。

TMyInteger = record
private
  Data: integer;
public
  class operator In(a: TMyInteger; b: array of integer): boolean;
end;
class operator TMyInteger.In(a: TMyInteger; b: array of integer): boolean;
begin
  Result:= false;
  for i in b do
    if a.data = i then exit(true);
end;

这允许使用以下语法:

if a in [500,600] then ....

有没有办法允许以下语法?

if a in [500..600] then ....     
//or some similar construct?

简短的回答是否定的,但你可以实现类似的东西,就像这样。它处理格式('5, 17-30, 69')等范围。请注意,我使用"-"而不是".."

请注意,我刚刚剪切和粘贴了我使用多年的函数 - 对于这个特定目的,您可能会做得更好。

unit UnitTest2;
interface
uses
  System.SysUtils;

type
TMyInteger = record
private
  Data: integer;
public
  class operator In(a: TMyInteger; const pVal : string): boolean;
end;
implementation
{ TMyInteger }
type EDSMListError = class(Exception);
function SplitDSMList( var List : string;
                         var First : integer;
                         var Last : integer ) : boolean;
var
  i : integer;
  ProcessingLast : boolean;
begin
  // splits list of form like '1-3,5,9,11-23' and so on
  // Returns TRUE if there has been a split, and false otherwise.
  // Space characters are ignored
  // If the above string were passed, the return values would be
  // List = '5,9,11-23'
  // First = 1
  // Last = 3
  // return = TRUE
  // The next call would return
  // List = '9,11-23'
  // First = 5
  // Last = 5
  Result := FALSE;
  First := 0;
  Last := 0;
  ProcessingLast := FALSE;
  for i := 1 to Length( List ) do
  begin
    case List[i] of
      '0'..'9':
      begin
        if ProcessingLast then
        begin
          Last := Last * 10 + Ord(List[i]) - Ord('0');
          Result := TRUE;
        end
        else
        begin
          First := First * 10 + Ord(List[i]) - Ord('0');
          Last := First;
          Result := TRUE;
        end;
      end;
      '-':
      begin
        ProcessingLast := TRUE;
        Last := 0;
        Result := TRUE;
      end;
      ',':
      begin
        Result := TRUE;
        List := Copy( List, i + 1, Length( List ) - i);
        Exit;
      end;
      ' ':
        // ignore spaces
        ;
      else
        // illegal character
        raise EDSMListError.Create('Illegal character found in list "' + List + '"');
    end;
  end;
  // If we get here we have reached the end of the message, so...
  List := '';
end;
function ValueInDSMList( const List : string; const Val : integer ) : boolean;
var
  iList : string;
  iBegin, iEnd : integer;
begin
  iList := List;
  Result := FALSE;
  while SplitDSMList( iList, iBegin, iEnd ) do
  begin
    // assume sorted!
    if Val < iBegin then
    begin
      exit;
    end
    else if Val <= iEnd then
    begin
      Result := TRUE;
      exit;
    end;
  end;
end;
class operator TMyInteger.In(a: TMyInteger; const pVal: string): boolean;
begin
  Result := ValueInDSMList( pVal, a.Data );
end;
end.

然后你会使用类似的东西

如果 a 在"500-600"中,那么....

根据大卫的评论:像if a in [500..600]这样的结构是不可能的。

从性能的角度来看(至少 32 位),最好的替代解决方法是:
这也给出了一个非常干净和灵活的语法。

case a of 
  500..600: ;//do work 
end;
//or:
if InRange(a, 500,600) then

在 64 位中,复杂的 case 语句不会得到优化,因此不要在紧密循环中使用它。

在 x64 中,case需要 1 个 CPU 周期,InRange需要 4 个 CPU 周期1
因此,性能差异可以忽略不计。

1 使用RDTSCP来测量时序;单个周期是由于无序优化。

最新更新