2016-03-02 94 views
2

我可以声明如下过载来扩展集合的限制。子范围和操作符重载

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? 
+0

有,如果你是负责编译器的工程师;) –

+0

我可以做一个预处理器..... – Johan

+0

那你可以做的。记得要加线在一段时间:) –

回答

2

简短的答案是否定的,但你可以实现类似的东西,就像这样。它可以处理的范围,格式为(“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. 

你会再使用类似

如果在“500-600”,那么....

+0

正如@David指出的,我没有提及性能,如果你正在解析,这个构造可能真的很有用。所以+1。如果你已经解析过,那么语法非常干净。 – Johan

1

按照大卫的评论:构建像if a in [500..600]是不可能的。

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

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

在64位复杂的case语句没有得到优化,所以不要在紧密循环使用。

在64所述case需要1个CPU周期和InRange需要4个CPU周期。
性能差异可以忽略不计。

使用RDTSCP来测量时间;单一周期是由于无序优化造成的。

+0

你的答案有一个子范围加上一个额外的值,这个问题没有说明。随着问题的形成,一个“InRange()”替代方案将会是一个很好的竞争者。你有没有比较这两种方案的表现? –

+0

这似乎没有解决问题,它查找了一条if语句,并没有提及perf。 –