2015-11-30 18 views
6

Tôi có một cấu trúc mà trông giống như sau:lặp trên dlang struct

struct MultipartMessage { 
    ubyte[] mime, data; 
    Header header; 

    void setSender(string sender) { 
     header.sender = sender; 
    } 
    void setId(int id) { 
     header.id = id; 
    } 
} 

và tôi muốn để lặp qua nó, trong lớp khác với một cái gì đó như thế này:

struct Socket { 
    ... 

    void send(MultipartMessage msg) { 
     foreach (part; msg) { 
      sendPart(part); 
     } 
    } 

    ... 
} 

Đây có phải là khả thi? Tôi muốn sử dụng một cái gì đó tương tự như của Python __iter__ trong các MultipartMessage có thể trả lại các trường theo một thứ tự cụ thể, và lý tưởng thậm chí chạy một số mã bổ sung, như header.serialize().

Lý tưởng nhất là tôi muốn nói thêm một chức năng để MultipartMessage đó sẽ giống như thế này (giả):

ubyte[] __iter__() { 
    yield mime; 
    yield data; 
    yield header.serialize(); //header.serialize returns a ubyte[] 
} 
+0

Như đã nêu trong tài liệu hướng dẫn (http://dlang.org/spec/statement.html#ForeachStatement) có rất nhiều cách để đối phó với tuyên bố foreach. Cách dễ nhất có thể là phạm vi đầu vào, nhưng không có một cái nhìn rõ ràng về những gì bạn muốn mỗi lần lặp lại trả về thì khó có thể biết cách nào là tốt nhất cho trường hợp của bạn. Bạn có thể chính xác hơn không? – cym13

Trả lời

2

Điều gần nhất với những gì bạn muốn có lẽ là opApply.

Xem http://dlang.org/spec/statement.html Mục Foreach over Structs and Classes wit opApply

này sẽ làm việc:

int opApply(int delegate(ref ubyte[]) dg) { 
    int result = 0; 
    result = dg(mime); 
    result = dg(data); 
    ubyte[] header_bytes = header.serialize(); 
    result = dg(header_bytes); 
    return result; 
} 
8

Sử dụng tupleof:

foreach (ref part; msg.tupleof) 
    sendPart(part); 

này sẽ gọi sendPart với mime, dataheader (các trường của struct, theo thứ tự chúng đã được khai báo). Bạn có thể lọc các trường bằng cách kiểm tra loại của chúng bằng ví dụ: static if (!is(typeof(part) == Header)).

Để có được tên của trường, bạn có thể sử dụng __traits(identifier):

foreach (i, ref part; msg.tupleof) 
    writeln(__traits(identifier, msg.tupleof[i])); 

(__traits(identifier, part) sẽ trở part.)

Ngoài ra còn có __traits(allMembers), mà cũng trả về phương pháp.

+0

'tupleof' không phải là một sự tương tự tốt với' __iter__' của Python. –

+0

Có lẽ tôi hiểu nhầm câu hỏi, nhưng D '' ___iter '' là 'opApply'. –

+0

ý của bạn là gì bởi '__iter__' của D là' opApply'? Làm thế nào mà có thể được sử dụng để lặp qua một cấu trúc? –

2

Có một số cách để làm điều lặp trên các đối tượng trong D.


Một là để thực hiện các InputRange API. Phạm vi đầu vào tương tự như các trình lặp, nhưng có một API khác. Triển khai giao diện dải ô có nghĩa là bạn có thể sử dụng tất cả các chức năng std.range/std.algorithm trên đối tượng của mình, chẳng hạn như map, array, joiner và v.v.

D không có chức năng __iter__ để nhận trình lặp từ bộ sưu tập tùy ý, do đó bạn cần triển khai hàm trả về phạm vi nhập.

import std.range; 

auto bytes() { 
    return chain(mime, data, header.serialize); 
} 

này sẽ trả về một loạt ubyte đầu vào, bao gồm các byte trong mime, tiếp theo là các byte trong data, sau đó trong header.serialize.


Bạn cũng có thể triển khai phương pháp opApply trên cấu trúc của mình. opApply sẽ chỉ hoạt động với foreach, vì vậy bạn không thể sử dụng các phương pháp phạm vi với nó, nhưng nó cho phép bạn thực hiện những việc như thực thi vòng lặp trong các chuỗi riêng biệt.

Ý chính của opApply là D chuyển phần thân vòng lặp tới opApply làm hàm; nghĩa là, foreach(x; myObj) { body } được chuyển thành myObj.opApply((x) { body }).

void opApply(void delegate(ubyte[] part) loopbody) { 
    loopbody(mime); 
    loopbody(data); 
    loopbody(header.serialize()); 
} 

Tuy nhiên, thay vì một trong những lựa chọn, tôi khuyên bạn nên thực hiện một chức năng trên đối tượng của bạn mà phải mất một khoảng đầu ra và ghi dữ liệu vào nó.

Phạm vi đầu ra là đối tượng chấp nhận các đối tượng khác và thực hiện điều gì đó với chúng. Trong trường hợp này, dải đầu ra phải chấp nhận ubyte s, làm cho nó tương tự như một luồng đầu ra.

void serialize(Range)(ref Range outRange) if(isOutputRange!(Range, ubyte)) { 
    put(outRange, mime); -- `put` simply feeds data into the output range 
    put(outRange, data); 
    header.serialize(outRange); // No longer have to allocate/return a ubyte array 
} 

sử dụng Ví dụ, lưu trữ đầu ra vào một Appender, có thể được chuyển đổi thành một mảng:

import std.array; 

auto serializedDataAppender = appender!ubyte(); 
myMsg.serialize(serializedDataAppender); 
auto serializedData = serializedDataAppender.data; 

Nếu bạn thực hiện một loạt sản lượng trên ổ cắm của bạn, thì đó có nghĩa là giải pháp dải đầu ra không phải cấp phát bộ nhớ nào từ heap.


Check-out cuốn sách Programming in D (đặc biệt là các RangesMore Ranges phần) để biết về cách thực hiện dãy riêng bạn.