2010-07-20 44 views
8

Tôi hoàn toàn hiểu câu hỏi này đã được yêu cầu rất nhiều, nhưng tôi yêu cầu một biến thể cụ thể và tìm kiếm của tôi đã bỏ cuộc, vì tôi chỉ tìm thấy các thuật toán gắn thêm một câu hỏi vectơ hiện tại sang một vector khác, nhưng không được trả về từ một hàm.C++ nối thêm một vector khác

tôi có chức năng này liệt kê tất cả các file trong một thư mục:

vector<string> scanDir(const string& dir) 

có thể gọi chính nó trong nội bộ (đối với thư mục con).

Tôi cần ngắn cách thêm giá trị trả lại cho vectơ của người gọi. Tôi có một cái gì đó trong tâm trí của tôi như thế này (nhưng tất nhiên nó không tồn tại :():

vector<string> fileList; 
//... 
fileList.append(scanDir(subdirname)); 

Tôi lo sợ rằng lưu trữ các giá trị trả về và chèn nó trong filelist sẽ mang lại hiệu suất badness Những gì tôi có nghĩa là thế này. :

vector<string> temp(scanDir(subdirname)); 
copy(temp.begin(), temp.end(), back_inserter(fileList)); 

Cảm ơn

PS:!. tôi không ép buộc bản thân mình để sử dụng vector, bất kỳ container khác mà thực hiện tốt như nhau và có thể ngăn chặn các hoạt động tiềm năng bản sao lớn là tốt bởi tôi

+1

liên quan: http: //stackoverflow.com/questions/2208293/what-is-the-most-efficient-way-to-append-one-stdvector-to-the-end-of-another – kennytm

+4

Khi nói về hiệu suất, câu hỏi đầu tiên luôn là : bạn có đo? Sau đó, những điều tầm thường, là điều này đang được chạy trong một vòng lặp chặt chẽ, là hiệu suất ứng dụng của bạn quan trọng? Sao chép một vector so sánh với việc lấy danh sách các tệp từ hệ thống tệp? –

Trả lời

8

Nếu bạn đang ở vị trí để thay đổi scanDir, làm cho nó một chức năng (mẫu) chấp nhận một iterator đầu ra:

template <class OutIt> 
void scanDir(const std::string& dirname, OutIt it) { 
    // ... 
    // Scan subdir 
    scanDir(subdir, it); 
    // ... 
} 

Bạn sẽ có lợi ích bổ sung để có thể điền vào tất cả loại cấu trúc dữ liệu như

std::vector<string> vector; 
scanDir(dir1, std::back_inserter(vector)); 
std::set<string> fileset 
scanDir(dir1, std::inserter(fileset, fileset.begin())); 

, vv

EDIT (xem bình luận ...)

Để sử dụng chức năng này để khởi tạo thành viên lớp, bạn có thể hoặc gọi nó là trong các nhà xây dựng như trong

class MyClass { 
private: 
    std::vector<string> m_fileList; 
public: 
    MyClass(const std::string& dirname) { 
    scanDir(dirname, std::back_inserter(m_fileList); 
    } 
} 

hoặc sử dụng một hàm wrapper

std::vector<string> scanDir(const std::string& dirname) { 
    std::vector<string> result; 
    scanDir(dirname, std::back_inserter(result); 
    return result; 
} 

class MyClass { 
// Same as above.. 
    MyClass(const std::string& dirname) : m_fileList(scanDir(dirname)) { } 
} 

Tôi muốn phiên bản đầu tiên về hiệu suất (và các lý do khác) ...

+0

Tôi có thể sử dụng mẫu như vậy để khởi tạo một thành viên dữ liệu lớp (giữ danh sách tệp không?) ... oh chờ đợi, bạn kết hợp với sukru là khá mạnh (hàm thứ hai để tạo ra giá trị trả về). – rubenvb

+0

Chỉ cần viết một chỉnh sửa về điều này. Tôi sẽ đề nghị gọi hàm một cách rõ ràng trong hàm tạo, tôi không thể thấy bất kỳ hạn chế nào của phương pháp này ... – MartinStettner

+0

Nó thường được gọi là 'OutputIterator' thay vì' InsertIterator'. –

1

Thay vì

vector<string> temp(scanDir(subdirname)); 

bạn có thể làm

vector<string> const& temp = scanDir(subdirname); 

và điền đầy đủ các bản sao:

fileList.insert(fileList.end(), temp.begin(), temp.end()); 
+0

Không có khả năng tạo ra bất kỳ sự khác biệt nào, một khi bản sao của nhà xây dựng bản sao được hoàn thành với nó. Không có yêu cầu cho dòng đầu tiên của mã để làm bất cứ điều gì mà dòng thứ hai của mã không phải là cũng cần thiết để làm. –

15

Tại sao không chỉ vượt qua các vector như một cuộc tranh cãi? Sau đó, mọi lời gọi có thể nối thêm vào cùng một véc tơ, mà không cần sao chép. Hoặc tạo một lớp triển khai tích lũy các phần tử vào một đối tượng thành viên.

+0

+1. Đi qua như một đối số là những gì tôi làm trong mã hiệu suất nhạy cảm. – Dummy00001

+2

Đó là 'void scanDir (chuỗi const & dir, vector & result)'. – cape1232

+0

+1 để đơn giản. – Brian

0

Chức năng đệ quy sẽ phải sao chép mọi thứ nhiều lần, O (chiều sâu) chính xác (nghĩa là mọi thứ ở cấp độ lá sẽ được sao chép lại cho đến khi nó đến gốc).

Phương pháp tốt nhất sẽ được chia thành hai chức năng khác nhau:

vector<string> scanDir(string path) 
{ 
    vector<string> retval; 

    scanDir(path, &retval); 

    return retval; 
} 

static void scanDir(string path, vector<string>& output) 
{ 
    .. scan 
    .. append to output 
} 
+0

trong quá tải đầu tiên của bạn, một bản sao hoàn chỉnh là của retval được đảm bảo sẽ được thực hiện khi trở về. –

+0

@alexandre: làm cách nào để tránh điều đó khi sử dụng chức năng này để khởi tạo thành viên dữ liệu lớp? Sẽ không có một số phép thuật biên dịch tránh bản sao? – rubenvb

+0

@rubenvb: tối ưu hóa giá trị trả về diễn ra khi bạn quay trở lại với một hàm tạo. Sử dụng một lớp học thay vào đó, xem bình luận của MartinStettner. –

8

PS: Tôi không ép buộc bản thân mình để sử dụng vector, bất kỳ container khác mà thực hiện tốt như nhau và có thể ngăn chặn sự tiềm năng lớn sao chép hoạt động là tốt của tôi.

Vâng, nếu bạn sử dụng list và gọi a.splice(a.end(), b); bạn sẽ tránh hoàn toàn thao tác sao chép. Một list nói chung sẽ là một danh sách liên kết chứ không phải là một mảng như là trường hợp với một vector, do đó, điều này có rất nhiều hiệu suất và cách sử dụng tác động. Nhưng mối nối chạy trong O (1), vì vậy đó là một lợi ích tốt đẹp.

+0

http://stackoverflow.com/questions/1905417/array-vs-vector-vs-list – kennytm

0

Làm thế nào về một hàm trợ giúp?

template<class T> 
std::vector<T>& VectorAppend(std::vector<T> &target, const std::vector<T> &source) 
{ 
    size_t insertPos = target.size(); 
    target.resize(target.size() + source.size()); 
    std::copy(source.begin(), source.end(), target.begin() + insertPos); 
    return target; 
} 
+0

Có gì sai với một đơn giản 'std :: copy (source.begin(), source.end(), std :: back_inserter (mục tiêu)); 'thay vào đó? Nếu bạn đang cố gắng tối ưu hóa phân bổ, bạn luôn có thể gọi 'target.reserve (target.size() + source.size());' trước khi thực hiện. – sbi

2

Sử dụng tiêu chuẩn :: danh sách và nối thêm bằng std :: list :: splice.

Từ the docs for splice:

Các hoạt động không liên quan đến việc xây dựng hoặc phá hoại bất kỳ đối tượng phần tử và, ngoại trừ phiên bản thứ ba, nó được thực hiện trong thời gian liên tục.

2
vector<string> fileList; 
vector<string> temp(scanDir(subdirname)); 

fileList.insert(fileList.end(), temp.begin(), temp.end()); 

Tôi hy vọng điều đó sẽ giúp ích cho bạn.

+0

chèn là một hoạt động thời gian tuyến tính. Câu hỏi cụ thể cho biết đề xuất của bạn không phải là những gì anh ấy đang tìm kiếm. "Tôi sợ rằng việc lưu trữ giá trị trả lại và chèn nó vào fileList sẽ mang lại hiệu suất xấu." – cape1232

+0

"Tôi sợ", đối với tôi điều đó có nghĩa là anh ta không chắc chắn liệu điều này có gây ra hiệu suất xấu hay không. Thành thật mà nói, tôi không nghĩ rằng việc sử dụng insert() trong trường hợp này sẽ gây ra bất kỳ vấn đề hiệu suất nào. Dù sao, cảm ơn cho trừ -, -. – virious

+0

Điều này trả lời tiêu đề của câu hỏi trắng tốt. Vì vậy, nó sẽ được dễ dàng để tìm thấy giải pháp này, khi peole như tôi đến đây để tìm kiếm một giải pháp cho điều đó. – kuester2000

-1

Đây có thể không phải là giải pháp đơn giản nhất có thể, nhưng còn về việc làm gì đó tương đương với C# StringBuilder của C#?

Tạo list<vector<string> > sau đó bạn có thể nối tất cả các vectơ mà bạn nhận được từ các cuộc gọi của mình tới scanDir() vào danh sách.

Nếu bạn hoàn toàn phải có một véc tơ ở cuối, khi đó bạn có thể tạo một véc tơ mới, phân bổ nó đủ lớn để không cần phải thay đổi kích thước và lắp ráp sản phẩm hoàn chỉnh của bạn.

Ngoài ra, bạn có thể tạo ra một lớp mới (nếu cần có nguồn gốc từ vector <T>) và trong nội bộ sử dụng vector danh sách < <T> > để lưu trữ các yếu tố này. Sau đó, bạn sẽ chỉ làm cho các vòng lặp của bạn lặp qua các phần tử trong danh sách đầu tiên, sau đó khi nó đến cuối cho các phần tử trong danh sách tiếp theo, chỉ trả về container :: end khi bạn đến cuối danh sách cuối cùng.

+0

Đây là câu trả lời phức tạp nhất mà tôi chưa từng nghĩ đến ... – rubenvb

-1

Tôi biết điều này không trả lời câu hỏi của bạn trực tiếp, nhưng liên quan đến mục tiêu cơ bản của bạn, bạn có thể muốn chỉ cần thực hiện lại chức năng của mình dưới dạng tăng :: hệ thống tệp. Trình lặp thư mục đã được đệ quy, do đó bạn không cần phải thực hiện các cuộc gọi đệ quy của riêng bạn. Bạn chỉ có thể điền một danh sách trong một vòng lặp trên trình lặp.Có một thực hiện ví dụ về ls: http://www.boost.org/doc/libs/1_43_0/libs/filesystem/example/simple_ls.cpp

Bạn cũng có được lợi ích bổ sung (lý thuyết) nền tảng độc lập, áp dụng tương đối rộng (lỗi được ferreted ra nhanh hơn với việc áp dụng nhiều hơn), vv

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