Lambda auf vector anwenden
-
Ich habe eben mal ein map implementiert, was dem aus python und perl recht nahe kommt:
http://ideone.com/CnubMl
-
Cool.
-
DrakoXP schrieb:
Ich habe eben mal ein map implementiert, was dem aus python und perl recht nahe kommt:
http://ideone.com/CnubMlUpps, das geht ja echt simpler.

Hier ist imho nun aber das Problem dass auto nicht mehr geht.
Meisten möchte man ja nicht einen vector<float> auf eine list<int> mappen sondern einen vector auf einen vector, ein set auf set etc.Bei eher komplexeren Typen möchte ich wie bei den Scriptsprachen die einfache Schreibweise haben.
Vlt so?
#include <iterator> #include <algorithm> #include <vector> template<typename T> struct ContainerRebind; template<typename T> struct ContainerRebind<std::vector<T>> { template<typename U> using Rebind = std::vector<U>; }; template<typename Functor, typename Container> auto map(Functor f, Container const& c) -> typename ContainerRebind<Container>::template Rebind<decltype(f(*std::begin(c)))> { typename ContainerRebind<Container>::template Rebind<decltype(f(*std::begin(c)))> result; std::transform(std::begin(c), std::end(c), std::inserter(result, std::end(result)), f); return result; } #include <iostream> #include <cmath> int main() { std::vector<int> normal = { 1, 2, 3, 4, 5 }; auto sqrts = map([&](int v) { return std::sqrt(v); }, normal); for(auto cur : sqrts) { std::cout << cur << " "; } }Hat den gewaltigen Nachteil dass man für jeden Container einen Rebinder schreiben müsste.
Geht sicher schöner, erleuchtet mich oh Template Gurus.

-
Ethon schrieb:
DrakoXP schrieb:
Ich habe eben mal ein map implementiert, was dem aus python und perl recht nahe kommt:
http://ideone.com/CnubMlUpps, das geht ja echt simpler.

Das kommt immer noch darauf an, was man erreichen möchte.
Der Hauptoverhead in meiner Variante ist einfach der Proxy zur Ermittlung des Zielcontainertyps anhand des Rückgabetyps.
Diesen Effekt (Templateparameter anhand des angefragten Rückgabetyps herleiten)
kann man bisher leider nicht anders bewerkstelligen.Wie du bereits schreibst, erlaubt meine Variante auch mappings zwischen verschiedenen Container-Typen, solange diese foward-iteratoren und back_inserter unterstützen. Auch wenn du mir das vielleicht nicht glaubst, manchmal möchte man das tatsächlich

Ansonsten ist dein Ansatz auch nur bedingt besser lesbar

PS: capturest du in deinen Lambdas immer pauschal alles aus dem darüberliegenden Scope, auch wenn es unnötig ist?
-
DrakoXP schrieb:
Ethon schrieb:
DrakoXP schrieb:
Ich habe eben mal ein map implementiert, was dem aus python und perl recht nahe kommt:
http://ideone.com/CnubMlUpps, das geht ja echt simpler.

Das kommt immer noch darauf an, was man erreichen möchte.
Der Hauptoverhead in meiner Variante ist einfach der Proxy zur Ermittlung des Zielcontainertyps anhand des Rückgabetyps.
Diesen Effekt (Templateparameter anhand des angefragten Rückgabetyps herleiten)
kann man bisher leider nicht anders bewerkstelligen.Ich war nicht sarkastisch. Hatte nur kompliziertere Ideen.

DrakoXP schrieb:
Wie du bereits schreibst, erlaubt meine Variante auch mappings zwischen verschiedenen Container-Typen, solange diese foward-iteratoren und back_inserter unterstützen. Auch wenn du mir das vielleicht nicht glaubst, manchmal möchte man das tatsächlich

Ansonsten ist dein Ansatz auch nur bedingt besser lesbar

Geht schon, was man halt will.
Mir geht es primär um die Scriptsprachen-ähnliche Verwendung. Und da finde ich ganz cool es zb in Expressions verwenden zu können.Klar möchte man manchmal in andere Container umwandeln aber das imho recht selten.
DrakoXP schrieb:
PS: capturest du in deinen Lambdas immer pauschal alles aus dem darüberliegenden Scope, auch wenn es unnötig ist?
Ne, habe ich bei Version 1 doch auch nicht.
-
Ethon schrieb:
Geht schon, was man halt will.
Mir geht es primär um die Scriptsprachen-ähnliche Verwendung. Und da finde ich ganz cool es zb in Expressions verwenden zu können.Das wird auf gar keine Weise zufriedenstellend gehen, weil man in Skriptsprachen ueblicherweise nur einen normalen Containertypen hat, in den groesseren, objektorientierten Sprachen je nach Verwendung aus einer Vielzahl auswaehlt, muss der Typ dazu irgendwie angegeben werden, wenn man nicht zu stark einschraenken will.
-
Marthog schrieb:
Ethon schrieb:
Geht schon, was man halt will.
Mir geht es primär um die Scriptsprachen-ähnliche Verwendung. Und da finde ich ganz cool es zb in Expressions verwenden zu können.Das wird auf gar keine Weise zufriedenstellend gehen, weil man in Skriptsprachen ueblicherweise nur einen normalen Containertypen hat, in den groesseren, objektorientierten Sprachen je nach Verwendung aus einer Vielzahl auswaehlt, muss der Typ dazu irgendwie angegeben werden, wenn man nicht zu stark einschraenken will.
Ach, ich würde mich mit vector als Sequenztyp schon mal ganz zufrieden geben. Für sets unordered_set, für maps unoredred_maps und für Strings std::string.
Spezialfälle kann man ja speziell behandeln.
-
btw. lässt sich für meinen map_proxy auch noch ein templatebasierter Iterator basteln. dann packt man sich noch ein begin() und end() an den Proxy schon geht auch das bewehrte
vector<int> numbers = { 42, 23, 1337 }; for (auto && p : map([](int number) -> pair<int, bool> { return make_pair(number, !!(number % 2)); }, numbers) cout << p.first << " is " << (p.second ? "odd" : "even") << "\n";Damit müsste man die Ergebnisse nicht einmal in einen extra Container zwischenspeichern.
Allerdings bin ich gerade zu faul solche Iteratoren zu basteln
-
Iteratoren wären vermutlich sowieso eher angesagt. ZB Python gibt bei map ja keine Liste sondern ein map Objekt zurück das lazy mappt.
Schade dass die Stdlib immer mit diesen hässlichen Start-/Enditeratoren und nicht Ranges arbeitet.Besonders gut gefällt mir aber das Streamkonzept aus Java 8. Damit kann man sich dank der neuen Lambdas sehr gut ausdrücken und anscheinend optimieren die intern auch ganz gut. Wäre interessant das mal für C++ zu implementieren.
-
@DrakoXP: Warum gibst du den Rückgabewert redundanterweise an?
Im Prinzip geht das so:
#include <iostream> #include <vector> #include <type_traits> #include <iterator> template <typename Func, typename Iterator> class mapped_range { Func f; Iterator first, last; public: mapped_range(Func f, Iterator first, Iterator last) : f(f), first(first), last(last) {} class iterator : std::iterator<std::input_iterator_tag, typename std::iterator_traits<Iterator>::value_type, typename std::iterator_traits<Iterator>::difference_type, typename std::iterator_traits<Iterator>::pointer, typename std::iterator_traits<Iterator>::reference> { typename std::remove_reference<Func>::type const* f; Iterator i; friend class mapped_range; iterator(typename std::remove_reference<Func>::type const* f, Iterator i) : f(f), i(i) {} public: iterator& operator++() {++i; return *this;} iterator operator++(int) {auto tmp = *this; ++*this; return tmp;} auto operator*() const -> decltype((*f)(*i)) {return (*f)(*i);} auto operator*() -> decltype((*f)(*i)) {return (*f)(*i);} bool operator==(iterator const& rhs) const { return i == rhs.i; } bool operator!=(iterator const& rhs) const { return !(*this == rhs); } }; iterator begin() const {return {&f, first};} iterator end () const {return {nullptr, last};} }; template <typename Func, typename Range> auto map(Func f, Range&& r) -> mapped_range<Func, decltype(std::begin(r))> { return {f, std::begin(r), std::end(r)}; }Wobei, um den
operator->zu implementieren, der Funktionswert zwischengespeichert werden müsste. Ist ja nur eine grobe Skizze.
-
Arcoth schrieb:
@DrakoXP: Warum gibst du den Rückgabewert redundanterweise an?
Wo genau?
Btw. gibt deine Variante generell ein mapped_range zurück, was zwar seine Iteratoren hat, aber nicht diese einfache Schreibweise erlaubt:
WhateverContainer<SomeType> foo = map([](...) { /* ... */ }, bar); // oder sogar WhateverContainer<SomeType> foo; foo = map([](...) { /* ... */ }, bar);
-
DrakoXP schrieb:
Wo genau?
[](int number) -> pair<int, bool> { return make_pair(number, !!(number % 2)); }Btw. gibt deine Variante generell ein mapped_range zurück, was zwar seine Iteratoren hat, aber nicht diese einfache Schreibweise erlaubt:
Offensichtlich nicht. Ich habe einfach den Iterator-Teil vorgezeigt.
-
Hier mal ein anderer Vorschlag:
#include <boost/iterator/transform_iterator.hpp> template <typename Func, typename Iterator> class mapped_range { Func f; Iterator first, last; Func getFunctor() {return std::forward<Func>(f);} public: mapped_range(Func f, Iterator const& first, Iterator const& last) : f(std::forward<Func>(f)), first(first), last(last) {} using iterator = boost::transform_iterator<Func, Iterator>; iterator begin() {return {first, getFunctor()};} iterator end () {return {last, getFunctor()};} template <typename Container> operator Container() { return {begin(), end()}; } }; #include <iterator> #include <type_traits> template <typename Func, typename Range> auto map(Func&& f, Range& r) -> mapped_range<typename std::remove_reference<Func>::type, decltype(std::begin(r))> { return {std::forward<Func>(f), std::begin(r), std::end(r)}; } template <typename Func, typename Range> auto map_ref(Func& f, Range& r) -> mapped_range<Func&, decltype(std::begin(r))> { return {f, std::begin(r), std::end(r)}; } template <typename Func, typename Range> auto map_funptr(Func* f, Range& r) -> mapped_range<Func*, decltype(std::begin(r))> { return {f, std::begin(r), std::end(r)}; } #include <cmath> #include <iostream> #include <vector> #include <algorithm> #include <cstdio> int main() { std::vector<int> numbers = { 42, 23, 1337 }; for (p : map([](int number) { return std::make_pair(number, !!(number % 2)); }, numbers)) std::cout << p.first << " is " << (p.second ? "odd" : "even") << "\n"; std::vector<float> converted_numbers = map([] (int i) {return std::sin(float(i));}, numbers); for (f : converted_numbers) std::printf("%.4f, ", f); // Nathan kriegt Tollwut }
-
Sieht schon mal nicht schlecht aus. Was ich persönlich auf jeden Fall wollen würde ist Folgendes:
auto sqrts = map(std::sqrt, unique(filter(isEven, map(toInt, myStringVector))));Sollte mit Arcoths Lösung gut möglich sein.

Vor allem ohne sinnlose Temporaries. Top.Gerade Lust mich an eine kleine Lib für den Hausgebrauch zu setzen.

-
meine finale Version mit map und grep.
http://ideone.com/V4yixaDas to<type> ist drin, weilstd::string zu viele nicht-explizite Konstruktoren hat und der Compiler sich da nicht entscheiden kann

Desweiteren kommt das Ganze ohne Boost aus.
Für einfache Funktionszeiger an Stelle von Lambdas könnte man noch etwas einbauen
und ich fürchte, dass generic lambdas aus C++14 hier auch Probleme machen könnten,
aber so funktionieren schonmal einige fancy Sachen
-
DrakoXP schrieb:
Für einfache Funktionszeiger an Stelle von Lambdas könnte man noch etwas einbauen
und ich fürchte, dass generic lambdas aus C++14 hier auch Probleme machen könnten,
aber so funktionieren schonmal einige fancy Sachen
Geht doch bereits mit Funktionszeigern?