{"id":3390,"date":"2018-04-03T11:31:21","date_gmt":"2018-04-03T10:31:21","guid":{"rendered":"http:\/\/roboblog.fatal-fury.de\/?p=3390"},"modified":"2018-04-03T11:35:54","modified_gmt":"2018-04-03T10:35:54","slug":"c-guns-sort-mit-visitor","status":"publish","type":"post","link":"http:\/\/roboblog.fatal-fury.de\/?p=3390","title":{"rendered":"C++ Guns: sort() mit Visitor"},"content":{"rendered":"<p>Wen hat es nicht schon einmal gest\u00f6rt, dass std::sort() so transparent arbeitet? Manchmal muss man wissen welche Elemente beim Sortieren wohin verschoben werden. Sozusagen eine Permutation.<br \/>\nManchmal ist es m\u00f6glich, den zu sortierenden Daten eine ID mitzugeben. Aber oft nicht. Zum Beispiel arbeite ich mit 2D Punkten. Diese liegen nat\u00fcrlich sch\u00f6n dicht gepackt im std::vector, und die ID ist implizit durch die Position im vector gegeben. Durch das Sortieren w\u00fcrde sich auch die ID \u00e4ndern, was aber nicht geht, da sie mit andern Daten noch verkn\u00fcpft ist. Und die ID explicit abspeichern ist auch nicht gut, da dann die x\/y Koordinaten vom Punkt im Speicher nicht mehr optimal ausgerichtet sind. Das Speicherlayout \u00e4ndert sich zu meinen Ungunsten.<\/p>\n<p>Zumindest in meinem Anwendungsfall brauche ich die sortierten Daten auch nur kurzzeitig, und die original Daten sollen unver\u00e4ndert bleiben. Es w\u00fcrde teilweise auch vollkommen langen, wenn die sort Funktion nur ein vector<int> mit neuen IDs in der sortierten Reihenfolge zur\u00fcck gibt. <\/p>\n<p>Mein erster Ansatz ist, eine sort() Funktion zu schreiben, welche als zus\u00e4tzliches Argument einen Visitor akzeptiert. Diese Funktion ruft die eigentlich std::sort() Funktion auf, so dass alles vom Standard noch eingehalten wird und sich auch nichts an der asymptotischen Laufzeit \u00e4ndert.<br \/>\nUm eine ID zu erhalten, werte ich die Adresse der Elemente im std::vector aus. Damit die original Daten (erst einmal) in ihrer urspr\u00fcnglichen Reihenfolge bleiben, wird ein zweiter std::vector mit Referenz Wrappern erstellt. Dies kostet pro Element den Speicherplatz einer Referenz, also 8byte. Dies ist, denke ich, nie ein Problem.<\/p>\n<p>Nach der eigentlichen Sortierung mit std::sort ist so feststellbar, wie die Elemente im vector getauscht wurden. Damit wird das Visitor Objekt aufgerufen, so dass der Anweder den Permutationsvektor erstellen kann. Und die eigentlichen Daten werden durch swap sortiert.<\/p>\n<p>Hier der Code der ersten Implementierung:<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\ntemplate&lt;typename T, typename Allocator, typename Visitor&gt;\r\ninline void sort(std::vector&lt;T,Allocator&gt;&amp; data,  Visitor&amp;&amp; vis) {\r\n    std::vector&lt;std::reference_wrapper&lt;const T&gt;&gt; sorted;\r\n    sorted.reserve(data.size());\r\n    for(const auto&amp; x : data) {\r\n        sorted.push_back(std::cref(x));\r\n    }\r\n\r\n    std::sort(sorted.begin(), sorted.end());\r\n    \/\/ ptrdiff_t is the signed equivalent to size_t\r\n    for(size_t i=0; i &lt; sorted.size(); ++i) {\r\n        \/\/ get the address of an item in data, not sorted.\r\n        const size_t idx1 = std::addressof(sorted&#x5B;i].get()) - data.data();\r\n        if(i &lt; idx1) {\r\n            std::swap(data&#x5B;i], data&#x5B;idx1]);\r\n            vis(i, idx1);\r\n        }\r\n    }\r\n}\r\n\r\n\/\/\/\/\/\/\r\n\r\n    struct Visitor {\r\n        void operator()(size_t i, size_t j) {\r\n            std::cout &lt;&lt; &quot;Swap idx &quot; &lt;&lt; i &lt;&lt; &quot; with idx &quot; &lt;&lt; j &lt;&lt; &quot;\\n&quot;;\r\n        }\r\n    };\r\n\r\n    std::vector&lt;int&gt; v {3,2,1,0};\r\n    cpl::sort(v, Visitor());\r\n    QVERIFY(std::is_sorted(v.begin(), v.end()));\r\n<\/pre>\n<blockquote><p>Swap idx 0 with idx 3<br \/>\nSwap idx 1 with idx 2<\/p><\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p>Wen hat es nicht schon einmal gest\u00f6rt, dass std::sort() so transparent arbeitet? Manchmal muss man wissen welche Elemente beim Sortieren wohin verschoben werden. Sozusagen eine Permutation. Manchmal ist es m\u00f6glich, den zu sortierenden Daten eine ID mitzugeben. Aber oft nicht. Zum Beispiel arbeite ich mit 2D Punkten. Diese liegen nat\u00fcrlich sch\u00f6n dicht gepackt im std::vector, [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[17],"class_list":["post-3390","post","type-post","status-publish","format-standard","hentry","category-allgemein","tag-cpp"],"_links":{"self":[{"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/posts\/3390","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3390"}],"version-history":[{"count":4,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/posts\/3390\/revisions"}],"predecessor-version":[{"id":3394,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/posts\/3390\/revisions\/3394"}],"wp:attachment":[{"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3390"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3390"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3390"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}