2010-11-04 38 views
6

Tôi có một thuật toán tính toán trong Delphi với một số tùy chọn khác nhau và tôi cần thử mọi kết hợp các tùy chọn để tìm ra giải pháp tối ưu.Liệt kê các giá trị có thể đặt trong Delphi

TMyOption = (option1, option2, option3, option4); 
TMyOptions = set of TMyOption; 

Tôi băn khoăn về việc sử dụng một vòng lặp Integer để liệt kê chúng:

for EnumerationInteger := 0 to 15 do begin 
    Options := TMyOptions(EnumerationInteger); 
end; 

này không biên dịch. Những gì tôi đã tự hỏi là nếu có bất kỳ phương pháp khá đơn giản để chuyển đổi từ Integer to Set (hầu hết các câu hỏi trên Web cố gắng đi theo cách khác, từ Set to Integer), và nếu như vậy nó là gì?

Một khả năng khác là chỉ cần sử dụng Integer như một chút-lĩnh vực:

C_Option1 = 1; 
C_Option2 = 2; 
C_Option3 = 4; 
C_Option4 = 8; 

và sau đó kiểm tra thành viên với một Bitwise và:

if (Options and C_Option2) > 0 then begin 
    ... 
end; 

Tôi đã cố gắng này, và nó hoạt động , nhưng nó cảm thấy như làm việc với các bộ sẽ tự nhiên hơn và sử dụng hệ thống kiểu tốt hơn (mặc dù tôi đang đi ra ngoài hệ thống kiểu đã nói để liệt kê các bộ).

Có cách nào tốt hơn/an toàn hơn để liệt kê tất cả các tổ hợp có thể đặt hơn là liệt kê biểu diễn số nguyên cơ bản không?

Ghi chú:

  1. Tôi biết rằng các giá trị số nguyên của một bộ không đảm bảo về mặt lý thuyết (mặc dù tôi nghi ngờ họ đang có trong thực tế nếu bạn không chơi với cách đánh số liệt kê).
  2. Có thể có nhiều hơn bốn tùy chọn (có, tôi biết rằng nó phát triển theo cấp số nhân và nếu có quá nhiều tùy chọn thuật toán có thể mất mãi mãi).

Trả lời

4

Hãy thử

var EnumerationByte: Byte; 
... 
for EnumerationByte := 0 to 15 do begin 
    Options := TMyOptions(EnumerationByte); 
end; 
+0

Điều đó thực sự làm việc. Tôi cho rằng nó sẽ không hoạt động đối với các bộ với hơn 8 thành viên? (mặc dù vào thời điểm đó thuật toán có lẽ đã quá chậm). –

+1

Làm sizeof (SetVar) để tìm hiểu cách một tập hợp cụ thể được đại diện bởi Delphi. –

2

Mã của bạn không biên dịch vì liệt kê của bạn (TMyOption) còn lại ít hơn 8 giá trị, và Delphi sử dụng các kích thước có thể tối thiểu (tính theo byte) cho bộ. Vì vậy, một biến byte sẽ làm việc cho bạn.

Nếu bạn có tập hợp có nhiều hơn 8 nhưng ít hơn 16 thành phần có thể, một từ sẽ hoạt động (và không phải là số nguyên).

Đối với hơn 16 nhưng dưới 32 biến DWord và định dạng.

Đối với hơn 32 thành phần có thể, tôi nghĩ cách tiếp cận tốt hơn là sử dụng một mảng byte hoặc một cái gì đó tương tự.

+1

Vấn đề không phải là kích thước của số nguyên, vấn đề là anh ta đang cố gắng để cast * set * type (TMyOptions), không phải kiểu liệt kê. –

+0

Alberto: Tập hợp các kiểu byte-word-dword cast là tất nhiên có thể và có ý nghĩa trong trường hợp này. – jachguate

0

Sự cố là bạn đang cố truyền sang loại đã đặt thay vì loại được liệt kê. Bạn có thể bỏ giữa các số nguyên và liệt kê bởi vì cả hai đều là các kiểu thứ tự, nhưng bạn không thể truyền vào một tập bởi vì chúng sử dụng bitfiels như bạn đã lưu ý. Nếu bạn sử dụng:

for EnumerationInteger := 0 to 15 do begin 
    Option := TMyOption(EnumerationInteger); 
end; 

nó sẽ hoạt động, mặc dù không phải là thứ bạn muốn.

Tôi đã gặp vấn đề này một vài tháng trước và đi đến kết luận rằng bạn không thể liệt kê nội dung của một bộ trong Delphi (ít nhất là trong Delphi 7) vì ngôn ngữ không xác định hoạt động trên một bộ .

Chỉnh sửa: Dường như bạn thậm chí có thể ở D7, hãy xem các câu trả lời cho câu trả lời này.

+0

Xem câu trả lời được chấp nhận. Sử dụng Byte thay vì một số nguyên không hoạt động. –

+1

erm ... Bạn CÓ THỂ liệt kê nội dung của một tập hợp. Nó chỉ phản trực giác. tức là Đối với tôi: = Thấp (Enum) đến cao (Enum) do /// nếu tôi trong Đặt sau đó. –

+2

Hoặc, như Delphi 2005, bạn có thể trực tiếp liệt kê các nội dung: 'cho el in s ...' –

0

500 - Câu trả lời của Lỗi máy chủ nội bộ có lẽ là câu trả lời đơn giản nhất.

Một cách tiếp cận khác ít có khả năng xảy ra với thay đổi về số lượng tùy chọn sẽ là khai báo một mảng boolean và bật/tắt chúng. Đây là chậm hơn so với làm việc với số nguyên tinh khiết mặc dù. Ưu điểm chính, bạn sẽ không cần phải thay đổi kiểu số nguyên bạn sử dụng, và bạn có thể sử dụng nó nếu bạn có nhiều hơn 32 tùy chọn.

procedure DoSomething 
var BoolFlags : Array[TOption] of Boolean; 
    I: TOption; 
    function GetNextFlagSet(var Bools : Array of Boolean) : Boolean; 
    var idx, I : Integer; 
    begin 
    idx := 0; 
    while Bools[idx] and (idx <= High(Bools)) do Inc(idx); 

    Result := idx <= High(Bools); 

    if Result then 
     for I := 0 to idx do 
     Bools[I] := not Bools[I]; 
    end; 
begin 
    for I := Low(BoolFlags) to High(BoolFlags) do BoolFlags[i] := False; 

    repeat 
    if BoolFlags[Option1] then 
     [...] 

    until not GetNextFlagSet(BoolFlags); 
end; 
0

Đúc từ một Integer để một Set là không thể, nhưng Tondrej đã từng viết một blog article trên SetToStringStringToSet đó cho thấy nhiều những gì bạn muốn trong SetOrdValue phương pháp:

uses 
    TypInfo; 

procedure SetOrdValue(Info: PTypeInfo; var SetParam; Value: Integer); 
begin 
    case GetTypeData(Info)^.OrdType of 
    otSByte, otUByte: 
     Byte(SetParam) := Value; 
    otSWord, otUWord: 
     Word(SetParam) := Value; 
    otSLong, otULong: 
     Integer(SetParam) := Value; 
    end; 
end; 

Mã của bạn sau đó sẽ trở thành này:

for EnumerationInteger := 0 to 15 do begin 
    SetOrdValue(TypeInfo(TMyOptions), Options, EnumerationInteger); 
end; 

--jeroen

6

Tôi biết câu hỏi này là khá cũ, nhưng đây là sở thích của tôi vì nó đơn giản và tự nhiên với tôi:

function NumericToMyOptions(n: integer): TMyOptions; 
var 
    Op: TMyOption; 
begin 
    Result:= []; 
    for Op:= Low(TMyOption) to High(TMyOption) do 
    if n and (1 shl ord(Op)) > 0 then Include(Result, Op); 
end; 
+0

Rất đẹp, làm cho nó trông rất nhiều (mặc dù tôi có thể gắn bó với khác chỉ vì nó nhanh hơn). –

Các vấn đề liên quan