2011-09-16 24 views
5

Tôi biết đó là một ý tưởng rất tồi, vì vậy các đề xuất khác về cách thực hiện hiệu quả sẽ được đón nhận tốt.Trả lại một vectơ trống của chuỗi nếu không tìm thấy khóa

Đây là điều. Tôi có map<string,vector<string> >, tôi muốn tìm kiếm khóa và trả lại giá trị tương ứng của nó (vectơ của chuỗi trong trường hợp này). Lý do tôi nhấn mạnh vào trở lại (thay vì chỉ lặp lại) là tôi cần phải tìm kiếm các giá trị trả lại trong một số vector khác.

Một ví dụ sẽ làm cho điều này rõ ràng:

Input: 

key1 ---> {2,3,4} 
key2 ---> {1} 
key3 ---> {2,12,11,9} 

Đối key1 như đầu vào, vector với các giá trị 2,3,4 nên được trả lại. Bây giờ, các giá trị 2,3,4 này cần phải được tìm kiếm trong vector khác của chuỗi. Cách hiệu quả nhất để làm điều này là gì?

tôi đã cố gắng một cái gì đó như thế này:

vector<string> returnEdges(string key) 
{ 
    for (map<string, vector<string> >::iterator it=outgoing.begin(); 
    it!=outgoing.end();++it) 
    { 
     if (key.compare((*it).first)==0) 
     { 
      return (*it).second; 
     } 
    } 


    //return string<;//what should I return here???? 


} 

1) Làm thế nào tôi nên trở về vector trống trong trường hợp chính là không tìm thấy?

2) cách tốt nhất để thực hiện điều này là gì?

Tôi hy vọng câu hỏi là rõ ràng.

EDIT: Khi tôi viết câu hỏi, tôi nghĩ tại sao không trả về một trình lặp? Những người ở SO có chấp nhận ý tưởng này không?

Trả lời

4

1) Trả về trình lặp là một ý tưởng hay. Khi bạn làm điều này, cách tự nhiên để chỉ ra trường hợp "không tìm thấy" là trả về trình lặp .end(). Điều này có nhược điểm là trừu tượng có phần bị rò rỉ: người gọi phải có khả năng nhận được giá trị này .end() để so sánh với nó để kiểm tra lỗi, và trình lặp trở lại cho thấy giao diện phong phú hơn bạn muốn giống như (mã máy khách không thực sự chơi xung quanh với việc tăng và giảm trình lặp).

2) Trả về một véc-tơ rỗng cũng đơn giản như việc tạo một véc-tơ rỗng và trả lại. Tạo một vector rỗng = xây dựng một vectơ rỗng. Đây là những gì bạn nhận được từ - cuộn trống - hàm tạo mặc định của lớp vectơ.

3) Bạn không cần và không nên tự mình triển khai vòng tìm kiếm. Thư viện chuẩn đã triển khai thực hiện điều này cho bạn. (Có một find chức năng chuyên dùng cho map s vì sự khác biệt chính/giá trị. Đối với trình tự như list, vectordeque, thích chức năng miễn phí std::find, mà xuất phát từ <algorithm>.

4) Bạn nên thích chấp nhận chức năng các tham số (khi chúng là các cá thể của các lớp, như std::string) và trả về dữ liệu (đặc biệt là các thứ phức tạp như một vectơ chuỗi) bằng tham chiếu const. Việc chuyển và trả về bằng giá trị ngụ ý một bản sao; đôi khi trình biên dịch có thể tối ưu hóa điều này, nhưng nó không đáng tin cậy như chúng tôi muốn.Bên cạnh đó, lý do bạn đang sử dụng C++ ở nơi đầu tiên là có mức độ kiểm soát đó, đúng không? Nếu không, thì đừng tra tấn bản thân với nó.

Tuy nhiên, bạn không thể làm điều đó nếu bạn định trả lại giá trị mới được tạo ra một thời gian. Tuy nhiên, một cách khác để thiết kế giao diện là trả về một con trỏ tới vectơ của chuỗi (lưu ý rằng số học con trỏ trên các số này sẽ không hợp lệ) trong bản đồ hoặc con trỏ NULL nếu không tìm thấy giá trị. Điều này tránh sao chép và phân biệt kết quả "không tìm thấy" từ một vectơ trống thực tế trong dữ liệu, nhưng điều đó có nghĩa là mã khách hàng phải xử lý một con trỏ thô dữ liệu.

5) Việc 'trả lại' trong tên của hàm là vô ích, khi trả lại là chức năng thực hiện. OTOH, bạn nên đặt tên cho mọi thứ theo cách làm cho nó hiển nhiên tại sao các tham số là những gì chúng là.

6) Với trình vòng lặp cho các loại phức tạp, thường là một ý tưởng tốt để thiết lập typedef.

Việc trả lại iterator là đơn giản như:

typedef map<string, vector<string> >::iterator graph_iterator; 
graph_iterator edges_named(const string& node_name) { 
    return outgoing.find(node_name); 
} 

Trả về một vector của chuỗi cũng đơn giản như:

typedef map<string, vector<string> >::iterator graph_iterator; 
vector<string> edges_named(const string& node_name) { 
    graph_iterator it = outgoing.find(node_name); 
    return it == outgoing.end() ? vector<string>() : it->second; 
} 

Trả về một con trỏ cũng đơn giản như:

typedef map<string, vector<string> >::iterator graph_iterator; 
vector<string>* edges_named(const string& node_name) { 
    graph_iterator it = outgoing.find(node_name); 
    return it == outgoing.end() ? NULL : &(it->second); 
} 

Chọn một cách khôn ngoan.

+0

Tôi cảm thấy hơi lo lắng về những con trỏ thô đó. Có lẽ thiết kế đó có thể hữu ích, nhưng bằng cách nào đó phải có cách tốt hơn. OP được mời để giải thích các mục tiêu của cô ấy; có lẽ chúng ta có thể đề xuất một cái gì đó cụ thể. –

+0

@Karl, Câu trả lời tuyệt vời tuyệt vời. Tôi nghĩ rằng đây là những vấn đề mà nhiều người như tôi, những người đến từ các ngôn ngữ khác với mặt C++ và không thể sử dụng nó hiệu quả/hiệu quả – Anon

+0

Chuyển đổi vòng lặp sang con trỏ thô là một cách để hạn chế giao diện. Tuy nhiên, nó vẫn không ngăn việc tăng/giảm - nó chỉ có nghĩa là nó sẽ phá vỡ (hành vi không xác định ** dự kiến ​​** sẽ gặp sự cố bình thường, nhưng thực sự biết) thay vì cho phép người dùng bẻ khóa đóng gói mảnh bitty. Có lẽ một ý tưởng tốt hơn là tạo một lớp bao bọc - một loại con trỏ thông minh cố ý câm. :) –

2

Làm thế nào về điều này:

std::vector<std::string> find_by_key_maybe(const std::string & key) 
{ 
    std::map<std::string, std::vector<std::string>>::const_iterator it = themap.find(key); 
    return it == themap.end() ? std::vector<std::string>() : it->second; 
} 

Hoặc thậm chí, nếu themap là không const, và bạn cũng muốn thêm một vector rỗng vào bản đồ:

std::vector<std::string> find_by_key_and_insert_if_missing(const std::string & key) 
{ 
    return themap[key]; 
} 

Nó hoàn toàn OK để trở lại một vector theo giá trị, nếu đó là điều mà tình huống yêu cầu.

+0

Lưu ý rằng việc sử dụng 'themap [key]' cũng sẽ thêm vectơ trống vào bản đồ bên dưới khóa đó. Tùy thuộc vào ứng dụng chính xác của bạn, điều này có thể từ vô cùng hữu ích đến hoàn toàn không được chấp nhận và ở mọi nơi ở giữa. –

+0

@Karl: Hah, vâng - tôi nên thêm cái này. Cảm ơn! –

+0

Cảm ơn. những gì về trở về iterator? không hiệu quả hơn sao? (Tôi có thể sai ở đây) – Anon

1

Hầu hết thời gian bạn không cần bản sao của vectơ được trả về, tham chiếu const cũng sẽ hoạt động. Bạn luôn có thể sao chép sau nếu cần.

Ngoài ra, bạn không cần phải lặp qua bản đồ, chúng được tối ưu hóa để tìm kiếm bằng phương pháp find.

const vector<string> & returnEdges(string key) 
{ 
    map<string, vector<string> >::iterator it = outgoing.find(key); 
    if (it == outgoing.end()) 
    { 
     static vector<string> empty_vector; 
     return empty_vector; 
    } 
    return it->second; 
} 
0

Tôi nghĩ toán tử [] có thể giúp bạn, nó sẽ trả về giá trị rỗng (một vectơ trống ở đây). Vì vậy, tất cả những gì bạn cần làm là

vector<string> returnEdges(string key) 
{ 
    return outgoing[key]; 
} 
+0

Như chúng ta đã thảo luận ở trên, điều này cũng * chèn * một giá trị rỗng vào bản đồ, có thể hoặc không thể mong muốn. –

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