วิธีที่ทันสมัยในการกรอง STL container?


98

กลับมาที่ C ++ หลังจากหลายปีของ C # ฉันสงสัยว่าสิ่งที่ทันสมัย ​​- อ่าน: C ++ 11 - วิธีการกรองอาร์เรย์จะเป็นอย่างไรเช่นเราจะบรรลุสิ่งที่คล้ายกับแบบสอบถาม Linq นี้ได้อย่างไร:

var filteredElements = elements.Where(elm => elm.filterProperty == true);

เพื่อกรองเวกเตอร์ขององค์ประกอบ ( stringsสำหรับคำถามนี้)?

ฉันหวังเป็นอย่างยิ่งว่าอัลกอริทึมสไตล์ STL แบบเก่า (หรือแม้กระทั่งส่วนขยายเช่นboost::filter_iterator) ที่ต้องการกำหนดวิธีการที่ชัดเจนจะถูกแทนที่ด้วยในตอนนี้?


สิ่งนี้ดึงองค์ประกอบทั้งหมดที่filterPropertyตั้งค่าเป็นtrueหรือไม่
Joseph Mansfield

ขออภัยใช่ เกณฑ์ตัวกรองทั่วไป ..
ATV

3
นอกจากนี้ยังมีห้องสมุดบางอย่างที่พยายามเลียนแบบวิธีการของ LINQ .NET: Linq ++และcpplinq ฉันไม่ได้ทำงานกับพวกเขา แต่ฉันเดาว่าพวกเขารองรับคอนเทนเนอร์ STL
Dirk

1
คุณควรมีความชัดเจนมากขึ้นเกี่ยวกับสิ่งที่คุณต้องการเนื่องจากกลุ่มคนที่มีความสามารถทั้ง C ++ และ C # มีขนาดเล็ก อธิบายสิ่งที่คุณต้องการให้ทำ
Yakk - Adam Nevraumont

คำตอบ:


117

ดูตัวอย่างจาก cplusplus.com สำหรับstd::copy_if:

std::vector<int> foo = {25,15,5,-5,-15};
std::vector<int> bar;

// copy only positive numbers:
std::copy_if (foo.begin(), foo.end(), std::back_inserter(bar), [](int i){return i>=0;} );

std::copy_ifประเมินการแสดงออกแลมบ์ดาสำหรับทุกองค์ประกอบในfooที่นี่และถ้ามันกลับชุดมันคุ้มค่าที่จะtruebar

std::back_inserterช่วยให้เราสามารถจริงใส่องค์ประกอบใหม่ในตอนท้ายของbar(ใช้push_back()) กับ iterator โดยไม่ต้องปรับขนาดให้ได้ขนาดที่ต้องการแรก


30
นี่เป็นสิ่งที่ใกล้เคียงที่สุดกับ LINQ ที่ C ++ มีให้หรือไม่? นี่คือความกระตือรือร้น (IOW ไม่ขี้เกียจ) และฟุ่มเฟื่อยมาก
usr

1
@usr มันเป็นน้ำตาลไวยากรณ์ IMO ซึ่งเป็นวิธีที่ง่ายสำหรับการวนซ้ำ (และมักจะช่วยให้หลีกเลี่ยงการคัดลอกได้)
Sebastian Hoffmann

1
ตัวอย่าง OPs ไม่ใช้น้ำตาลซินแทติกใด ๆ ของ LINQ ประโยชน์คือการประเมินและความสามารถในการประกอบ
usr

1
@usr ซึ่งยังคงสามารถทำได้โดยการ for-loop อย่างง่ายๆstd::copy_ifไม่เกิน for-loop
Sebastian Hoffmann

13
@Paranaix ทุกอย่างสามารถพูดได้ว่าเป็นเพียงน้ำตาลสังเคราะห์มากกว่าการประกอบ ประเด็นคือไม่ต้องเขียนสำหรับลูปเมื่ออัลกอริทึมสามารถประกอบได้อย่างชัดเจนด้วยวิธีที่อ่านได้โดยใช้การดำเนินการแบบดั้งเดิม (เช่นตัวกรอง) หลายภาษามีคุณสมบัติดังกล่าว - ใน C ++ น่าเสียดายที่มันยังคงมีปัญหา
BartoszKP

47

วิธีการที่มีประสิทธิภาพมากขึ้นหากคุณไม่ต้องการสำเนารายการใหม่ก็คือ remove_ifการนำองค์ประกอบออกจากคอนเทนเนอร์เดิม


7
@ATV ฉันชอบเป็นremove_ifพิเศษเพราะเป็นวิธีการใช้ตัวกรองเมื่อมีการกลายพันธุ์ซึ่งเร็วกว่าการคัดลอกรายการใหม่ทั้งหมด ถ้าฉันใช้ตัวกรองใน C ++ ฉันจะใช้สิ่งนี้copy_ifดังนั้นฉันคิดว่ามันเพิ่ม
djhaskin987

16
สำหรับเวกเตอร์อย่างน้อยremove_ifก็ไม่เปลี่ยนsize(). คุณจะต้องเชื่อมโยงกับeraseสิ่งนั้น
rampion

5
@rampion เย้ .. ลบ / ลบ. อีกหนึ่งความงามที่ทำให้ฉันรู้สึกเหมือนกำลังเจาะรูเทปเมื่อทำงานในภาษา C ++ (เมื่อเทียบกับภาษาสมัยใหม่) ทุกวันนี้ ;-)
ATV

1
การลบอย่างชัดเจนเป็นคุณลักษณะ คุณไม่จำเป็นต้องลบในทุกกรณี บางครั้งตัวทำซ้ำก็เพียงพอที่จะดำเนินการต่อ ในกรณีเช่นนี้การลบโดยปริยายจะทำให้เกิดค่าใช้จ่ายที่ไม่จำเป็น นอกจากนี้ไม่ใช่ว่าทุกคอนเทนเนอร์จะปรับขนาดได้ std :: array เช่นไม่มีวิธีลบเลย
Martin Fehrs

31

ใน C ++ 20 ใช้มุมมองตัวกรองจากไลบรารีช่วง: (ต้องใช้#include <ranges>)

// namespace views = std::ranges::views;
vec | views::filter([](int a){ return a % 2 == 0; })

ส่งคืนองค์ประกอบคู่อย่างเกียจคร้านใน vecอย่างเฉื่อยชาส่งกลับองค์ประกอบแม้ใน

(ดู[range.adaptor.object] / 4และ[range.filter] )


GCC 10 รองรับอยู่แล้ว ( การสาธิตสด ) สำหรับ Clang และ GCC เวอร์ชันเก่าคุณสามารถใช้ไลบรารี range-v3 ดั้งเดิมได้เช่นกันโดยใช้#include <range/v3/view/filter.hpp>(หรือ#include <range/v3/all.hpp>) และranges::viewsเนมสเปซแทนstd::ranges::views( การสาธิตสด )


คุณควรระบุ #include และใช้เนมสเปซที่จำเป็นสำหรับคำตอบในการรวบรวม นอกจากนี้คอมไพเลอร์ใดที่สนับสนุนสิ่งนี้ ณ วันนี้?
gsimard

2
@gsimard ดีกว่าตอนนี้?
LF

1
หากใครลองทำใน macOS: ณ เดือนพฤษภาคม 2020 libc ++ไม่รองรับสิ่งนี้
dax

25

ฉันคิดว่าBoost.Rangeสมควรได้รับการกล่าวถึงเช่นกัน โค้ดผลลัพธ์ค่อนข้างใกล้เคียงกับต้นฉบับ:

#include <boost/range/adaptors.hpp>

// ...

using boost::adaptors::filtered;
auto filteredElements = elements | filtered([](decltype(elements)::value_type const& elm)
    { return elm.filterProperty == true; });

ข้อเสียเพียงอย่างเดียวคือต้องประกาศประเภทพารามิเตอร์ของแลมบ์ดาอย่างชัดเจน ฉันใช้ Decltype (องค์ประกอบ) :: value_type เพราะหลีกเลี่ยงการสะกดประเภทที่แน่นอนและยังเพิ่มเกรนทั่วไป หรืออีกวิธีหนึ่งด้วยแลมด้าโพลีมอร์ฟิคของ C ++ 14 ประเภทนี้สามารถระบุได้ว่าเป็นอัตโนมัติ:

auto filteredElements = elements | filtered([](auto const& elm)
    { return elm.filterProperty == true; });

filteredElements จะเป็นช่วงที่เหมาะสำหรับการส่งผ่าน แต่โดยทั่วไปแล้วจะเป็นมุมมองของคอนเทนเนอร์ดั้งเดิม หากสิ่งที่คุณต้องการคือคอนเทนเนอร์อื่นที่เต็มไปด้วยสำเนาขององค์ประกอบที่ตรงตามเกณฑ์ (เพื่อให้เป็นอิสระจากอายุการใช้งานของคอนเทนเนอร์เดิม) อาจมีลักษณะดังนี้:

using std::back_inserter; using boost::copy; using boost::adaptors::filtered;
decltype(elements) filteredElements;
copy(elements | filtered([](decltype(elements)::value_type const& elm)
    { return elm.filterProperty == true; }), back_inserter(filteredElements));

12

คำแนะนำของฉันสำหรับ C ++ เทียบเท่ากับ C #

var filteredElements = elements.Where(elm => elm.filterProperty == true);

กำหนดฟังก์ชันเทมเพลตที่คุณส่งเพรดิเคตแลมบ์ดาเพื่อทำการกรอง ฟังก์ชันเทมเพลตจะส่งกลับผลลัพธ์ที่กรองแล้ว เช่น:

template<typename T>
vector<T> select_T(const vector<T>& inVec, function<bool(const T&)> predicate)
{
  vector<T> result;
  copy_if(inVec.begin(), inVec.end(), back_inserter(result), predicate);
  return result;
}

ที่จะใช้ - ให้ตัวอย่างเล็กน้อย:

std::vector<int> mVec = {1,4,7,8,9,0};

// filter out values > 5
auto gtFive = select_T<int>(mVec, [](auto a) {return (a > 5); });

// or > target
int target = 5;
auto gt = select_T<int>(mVec, [target](auto a) {return (a > target); });

11

ปรับปรุงรหัสpjmตามคำแนะนำขีดล่าง -d :

template <typename Cont, typename Pred>
Cont filter(const Cont &container, Pred predicate) {
    Cont result;
    std::copy_if(container.begin(), container.end(), std::back_inserter(result), predicate);
    return result;
}

การใช้งาน:

std::vector<int> myVec = {1,4,7,8,9,0};

auto filteredVec = filter(myVec, [](int a) { return a > 5; });
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.