2016-03-02 93 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。 –