How to parametrize an algorithm with a custom non-pure swap-function?
I want to apply an algorithm from <algorithms> on ranges of elements, containing in one container, defined by iterator pairs, containing into another one. To do this I need a swap function with state: just pointer to the container with elements to be able to make swaps synchronously in both containers.
This is my incomplete attempt:
#include <utility>
#include <algorithm>
#include <vector>
#include <list>
#include <iostream>
#include <random>
inline
std::ostream & operator << (std::ostream & out, const std::pair< int, int > & p)
{
return out << '{' << p.first << ", " << p.second << '}';
}
int main()
{
using L = std::list< std::pair< int, int > >;
using I = typename L::const_iterator;
using P = std::pair< I, I >;
using R = std::vector< P >;
L values;
R ranges;
auto l = std::cbegin(values);
for (int i = 0; i < 10; ++i) {
l = values.emplace(std::cend(values), i, 0);
auto & p = ranges.emplace_back(l, l);
for (int j = 1; j <= i; ++j) {
p.second = values.emplace(std::cend(values), i, j);
}
}
const auto swap = [&values] (P & l, P & r)
{
auto ll = std::next(l.second);
auto rr = std::next(r.second);
if (ll == r.first) {
values.splice(rr, values, l.first, ll);
} else if (rr == l.first) {
values.splice(ll, values, r.first, rr);
} else {
L temp;
temp.splice(std::cend(temp), values, l.first, ll);
values.splice(ll, values, r.first, rr);
values.splice(rr, std::move(temp));
}
std::swap(l, r);
};
for (const auto & p : values) {
std::cout << p << std::endl;
}
std::cout << "-----" << std::endl;
std::shuffle(std::begin(ranges), std::end(ranges), std::mt19937{std::random_device{}()}); // just an example, it can be any algo, say std::sort w/ custom comparator
for (const auto & p : values) {
std::cout << p << std::endl;
}
}
For sure the swap function object from the above code cannot "participate in overload resolution" (it is oxymoron in the current context because of many reasons, please, do not focus on this).
What I can to do is to define a tagged version of iterator pair in an namespace scope (global (either named or anonymous one, not matters much)) like this using P = struct { std::pair< I, I > p }; and overloading of the free function void swap(P & l, P & r); with the body of the lambda from the code above. Also I surely should to make values a global variable. It leads to hindering of the usefulness of the approach from the code above.
Is there a way to pass a stateful swap function to the algorithms from <algorithm> in a more generic way, then described above?
I read the article and draft about customization points of Eric Niebler. But his approach imply modification of the STL. Either way even if it would be the point his approach cannot allow me to pass stateful overloadings from the function scope, I think, isn't it?
c++ algorithm lambda stl overload-resolution
|
show 2 more comments
I want to apply an algorithm from <algorithms> on ranges of elements, containing in one container, defined by iterator pairs, containing into another one. To do this I need a swap function with state: just pointer to the container with elements to be able to make swaps synchronously in both containers.
This is my incomplete attempt:
#include <utility>
#include <algorithm>
#include <vector>
#include <list>
#include <iostream>
#include <random>
inline
std::ostream & operator << (std::ostream & out, const std::pair< int, int > & p)
{
return out << '{' << p.first << ", " << p.second << '}';
}
int main()
{
using L = std::list< std::pair< int, int > >;
using I = typename L::const_iterator;
using P = std::pair< I, I >;
using R = std::vector< P >;
L values;
R ranges;
auto l = std::cbegin(values);
for (int i = 0; i < 10; ++i) {
l = values.emplace(std::cend(values), i, 0);
auto & p = ranges.emplace_back(l, l);
for (int j = 1; j <= i; ++j) {
p.second = values.emplace(std::cend(values), i, j);
}
}
const auto swap = [&values] (P & l, P & r)
{
auto ll = std::next(l.second);
auto rr = std::next(r.second);
if (ll == r.first) {
values.splice(rr, values, l.first, ll);
} else if (rr == l.first) {
values.splice(ll, values, r.first, rr);
} else {
L temp;
temp.splice(std::cend(temp), values, l.first, ll);
values.splice(ll, values, r.first, rr);
values.splice(rr, std::move(temp));
}
std::swap(l, r);
};
for (const auto & p : values) {
std::cout << p << std::endl;
}
std::cout << "-----" << std::endl;
std::shuffle(std::begin(ranges), std::end(ranges), std::mt19937{std::random_device{}()}); // just an example, it can be any algo, say std::sort w/ custom comparator
for (const auto & p : values) {
std::cout << p << std::endl;
}
}
For sure the swap function object from the above code cannot "participate in overload resolution" (it is oxymoron in the current context because of many reasons, please, do not focus on this).
What I can to do is to define a tagged version of iterator pair in an namespace scope (global (either named or anonymous one, not matters much)) like this using P = struct { std::pair< I, I > p }; and overloading of the free function void swap(P & l, P & r); with the body of the lambda from the code above. Also I surely should to make values a global variable. It leads to hindering of the usefulness of the approach from the code above.
Is there a way to pass a stateful swap function to the algorithms from <algorithm> in a more generic way, then described above?
I read the article and draft about customization points of Eric Niebler. But his approach imply modification of the STL. Either way even if it would be the point his approach cannot allow me to pass stateful overloadings from the function scope, I think, isn't it?
c++ algorithm lambda stl overload-resolution
Rather thanrangesbeing a container ofstd::pair<I, I>, why not ofstruct { I first; I second; L & list; }?
– Caleth
Nov 23 '18 at 9:17
As an aside, all yourp.seconds are elements of L of the formi, i. Do you intend that?
– Caleth
Nov 23 '18 at 9:25
1
@Caleth It is one of thinkable solutions, but it leads to extra data duplication.
– Orient
Nov 23 '18 at 9:33
@Calethstd::pair< int, int >elements are just for example.firstis just a number of a range here and thesecondis an unique value within the range. It is intended for ease of visual distinction. All ranges are of different lengths.
– Orient
Nov 23 '18 at 9:40
Forstruct { I first; I second; L * list = {}; }one also need to overload move constructor and move assignment operator to interoperate w/std::swapproperly.
– Orient
Nov 23 '18 at 10:28
|
show 2 more comments
I want to apply an algorithm from <algorithms> on ranges of elements, containing in one container, defined by iterator pairs, containing into another one. To do this I need a swap function with state: just pointer to the container with elements to be able to make swaps synchronously in both containers.
This is my incomplete attempt:
#include <utility>
#include <algorithm>
#include <vector>
#include <list>
#include <iostream>
#include <random>
inline
std::ostream & operator << (std::ostream & out, const std::pair< int, int > & p)
{
return out << '{' << p.first << ", " << p.second << '}';
}
int main()
{
using L = std::list< std::pair< int, int > >;
using I = typename L::const_iterator;
using P = std::pair< I, I >;
using R = std::vector< P >;
L values;
R ranges;
auto l = std::cbegin(values);
for (int i = 0; i < 10; ++i) {
l = values.emplace(std::cend(values), i, 0);
auto & p = ranges.emplace_back(l, l);
for (int j = 1; j <= i; ++j) {
p.second = values.emplace(std::cend(values), i, j);
}
}
const auto swap = [&values] (P & l, P & r)
{
auto ll = std::next(l.second);
auto rr = std::next(r.second);
if (ll == r.first) {
values.splice(rr, values, l.first, ll);
} else if (rr == l.first) {
values.splice(ll, values, r.first, rr);
} else {
L temp;
temp.splice(std::cend(temp), values, l.first, ll);
values.splice(ll, values, r.first, rr);
values.splice(rr, std::move(temp));
}
std::swap(l, r);
};
for (const auto & p : values) {
std::cout << p << std::endl;
}
std::cout << "-----" << std::endl;
std::shuffle(std::begin(ranges), std::end(ranges), std::mt19937{std::random_device{}()}); // just an example, it can be any algo, say std::sort w/ custom comparator
for (const auto & p : values) {
std::cout << p << std::endl;
}
}
For sure the swap function object from the above code cannot "participate in overload resolution" (it is oxymoron in the current context because of many reasons, please, do not focus on this).
What I can to do is to define a tagged version of iterator pair in an namespace scope (global (either named or anonymous one, not matters much)) like this using P = struct { std::pair< I, I > p }; and overloading of the free function void swap(P & l, P & r); with the body of the lambda from the code above. Also I surely should to make values a global variable. It leads to hindering of the usefulness of the approach from the code above.
Is there a way to pass a stateful swap function to the algorithms from <algorithm> in a more generic way, then described above?
I read the article and draft about customization points of Eric Niebler. But his approach imply modification of the STL. Either way even if it would be the point his approach cannot allow me to pass stateful overloadings from the function scope, I think, isn't it?
c++ algorithm lambda stl overload-resolution
I want to apply an algorithm from <algorithms> on ranges of elements, containing in one container, defined by iterator pairs, containing into another one. To do this I need a swap function with state: just pointer to the container with elements to be able to make swaps synchronously in both containers.
This is my incomplete attempt:
#include <utility>
#include <algorithm>
#include <vector>
#include <list>
#include <iostream>
#include <random>
inline
std::ostream & operator << (std::ostream & out, const std::pair< int, int > & p)
{
return out << '{' << p.first << ", " << p.second << '}';
}
int main()
{
using L = std::list< std::pair< int, int > >;
using I = typename L::const_iterator;
using P = std::pair< I, I >;
using R = std::vector< P >;
L values;
R ranges;
auto l = std::cbegin(values);
for (int i = 0; i < 10; ++i) {
l = values.emplace(std::cend(values), i, 0);
auto & p = ranges.emplace_back(l, l);
for (int j = 1; j <= i; ++j) {
p.second = values.emplace(std::cend(values), i, j);
}
}
const auto swap = [&values] (P & l, P & r)
{
auto ll = std::next(l.second);
auto rr = std::next(r.second);
if (ll == r.first) {
values.splice(rr, values, l.first, ll);
} else if (rr == l.first) {
values.splice(ll, values, r.first, rr);
} else {
L temp;
temp.splice(std::cend(temp), values, l.first, ll);
values.splice(ll, values, r.first, rr);
values.splice(rr, std::move(temp));
}
std::swap(l, r);
};
for (const auto & p : values) {
std::cout << p << std::endl;
}
std::cout << "-----" << std::endl;
std::shuffle(std::begin(ranges), std::end(ranges), std::mt19937{std::random_device{}()}); // just an example, it can be any algo, say std::sort w/ custom comparator
for (const auto & p : values) {
std::cout << p << std::endl;
}
}
For sure the swap function object from the above code cannot "participate in overload resolution" (it is oxymoron in the current context because of many reasons, please, do not focus on this).
What I can to do is to define a tagged version of iterator pair in an namespace scope (global (either named or anonymous one, not matters much)) like this using P = struct { std::pair< I, I > p }; and overloading of the free function void swap(P & l, P & r); with the body of the lambda from the code above. Also I surely should to make values a global variable. It leads to hindering of the usefulness of the approach from the code above.
Is there a way to pass a stateful swap function to the algorithms from <algorithm> in a more generic way, then described above?
I read the article and draft about customization points of Eric Niebler. But his approach imply modification of the STL. Either way even if it would be the point his approach cannot allow me to pass stateful overloadings from the function scope, I think, isn't it?
c++ algorithm lambda stl overload-resolution
c++ algorithm lambda stl overload-resolution
edited Nov 23 '18 at 12:24
Orient
asked Nov 23 '18 at 9:07
OrientOrient
6,26153295
6,26153295
Rather thanrangesbeing a container ofstd::pair<I, I>, why not ofstruct { I first; I second; L & list; }?
– Caleth
Nov 23 '18 at 9:17
As an aside, all yourp.seconds are elements of L of the formi, i. Do you intend that?
– Caleth
Nov 23 '18 at 9:25
1
@Caleth It is one of thinkable solutions, but it leads to extra data duplication.
– Orient
Nov 23 '18 at 9:33
@Calethstd::pair< int, int >elements are just for example.firstis just a number of a range here and thesecondis an unique value within the range. It is intended for ease of visual distinction. All ranges are of different lengths.
– Orient
Nov 23 '18 at 9:40
Forstruct { I first; I second; L * list = {}; }one also need to overload move constructor and move assignment operator to interoperate w/std::swapproperly.
– Orient
Nov 23 '18 at 10:28
|
show 2 more comments
Rather thanrangesbeing a container ofstd::pair<I, I>, why not ofstruct { I first; I second; L & list; }?
– Caleth
Nov 23 '18 at 9:17
As an aside, all yourp.seconds are elements of L of the formi, i. Do you intend that?
– Caleth
Nov 23 '18 at 9:25
1
@Caleth It is one of thinkable solutions, but it leads to extra data duplication.
– Orient
Nov 23 '18 at 9:33
@Calethstd::pair< int, int >elements are just for example.firstis just a number of a range here and thesecondis an unique value within the range. It is intended for ease of visual distinction. All ranges are of different lengths.
– Orient
Nov 23 '18 at 9:40
Forstruct { I first; I second; L * list = {}; }one also need to overload move constructor and move assignment operator to interoperate w/std::swapproperly.
– Orient
Nov 23 '18 at 10:28
Rather than
ranges being a container of std::pair<I, I>, why not of struct { I first; I second; L & list; }?– Caleth
Nov 23 '18 at 9:17
Rather than
ranges being a container of std::pair<I, I>, why not of struct { I first; I second; L & list; }?– Caleth
Nov 23 '18 at 9:17
As an aside, all your
p.seconds are elements of L of the form i, i. Do you intend that?– Caleth
Nov 23 '18 at 9:25
As an aside, all your
p.seconds are elements of L of the form i, i. Do you intend that?– Caleth
Nov 23 '18 at 9:25
1
1
@Caleth It is one of thinkable solutions, but it leads to extra data duplication.
– Orient
Nov 23 '18 at 9:33
@Caleth It is one of thinkable solutions, but it leads to extra data duplication.
– Orient
Nov 23 '18 at 9:33
@Caleth
std::pair< int, int > elements are just for example. first is just a number of a range here and the second is an unique value within the range. It is intended for ease of visual distinction. All ranges are of different lengths.– Orient
Nov 23 '18 at 9:40
@Caleth
std::pair< int, int > elements are just for example. first is just a number of a range here and the second is an unique value within the range. It is intended for ease of visual distinction. All ranges are of different lengths.– Orient
Nov 23 '18 at 9:40
For
struct { I first; I second; L * list = {}; } one also need to overload move constructor and move assignment operator to interoperate w/ std::swap properly.– Orient
Nov 23 '18 at 10:28
For
struct { I first; I second; L * list = {}; } one also need to overload move constructor and move assignment operator to interoperate w/ std::swap properly.– Orient
Nov 23 '18 at 10:28
|
show 2 more comments
2 Answers
2
active
oldest
votes
You can just include values in your range object.
struct Range { I first; I second; L & list; }
void swap(Range & l, Range & r)
{
assert(std::addressof(l.list) == std::addressof(r.list));
using std::swap;
auto ll = std::next(l.second);
auto rr = std::next(r.second);
if (ll == r.first) {
l.list.splice(rr, l.list, l.first, ll);
} else if (rr == l.first) {
l.list.splice(ll, l.list, r.first, rr);
} else {
L temp;
temp.splice(std::cend(temp), l.list, l.first, ll);
l.list.splice(ll, l.list, r.first, rr);
l.list.splice(rr, std::move(temp));
}
swap(l.first, r.first);
swap(l.second, r.second);
}
You meant defining the free function. I like your solution.
– Orient
Nov 23 '18 at 10:41
add a comment |
I find a better way (if compare with ones based on swap function overloading) to implement desired: two-pass way to apply changes. It gives just linear overhead, instead of the algorithm of interest's complexity.
#include <utility>
#include <algorithm>
#include <vector>
#include <list>
#include <iostream>
#include <random>
#include <cassert>
inline
std::ostream & operator << (std::ostream & out, const std::pair< int, int > & p)
{
return out << '{' << p.first << ", " << p.second << '}';
}
int main()
{
using L = std::list< std::pair< int, int > >;
using I = typename L::const_iterator;
using P = std::pair< I, I >;
using R = std::vector< P >;
L values;
R ranges;
auto l = std::cbegin(values);
for (int i = 0; i < 10; ++i) {
l = values.emplace(std::cend(values), i, 0);
auto & p = ranges.emplace_back(l, l);
for (int j = 1; j <= i; ++j) {
p.second = values.emplace(std::cend(values), i, j);
}
}
for (const auto & p : values) {
std::cout << p << std::endl;
}
std::cout << "-----" << std::endl;
std::shuffle(std::begin(ranges), std::end(ranges), std::mt19937{std::random_device{}()});
l = std::cbegin(values);
for (const auto & range : ranges) {
auto r = std::next(range.second);
if (l != range.first) {
values.splice(l, values, range.first, r);
}
l = r;
}
assert(l == std::cend(values));
for (const auto & p : values) {
std::cout << p << std::endl;
}
}
Don't think it can be applicable to containers with somehow stricter iterators invalidation rules.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53443555%2fhow-to-parametrize-an-algorithm-with-a-custom-non-pure-swap-function%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
You can just include values in your range object.
struct Range { I first; I second; L & list; }
void swap(Range & l, Range & r)
{
assert(std::addressof(l.list) == std::addressof(r.list));
using std::swap;
auto ll = std::next(l.second);
auto rr = std::next(r.second);
if (ll == r.first) {
l.list.splice(rr, l.list, l.first, ll);
} else if (rr == l.first) {
l.list.splice(ll, l.list, r.first, rr);
} else {
L temp;
temp.splice(std::cend(temp), l.list, l.first, ll);
l.list.splice(ll, l.list, r.first, rr);
l.list.splice(rr, std::move(temp));
}
swap(l.first, r.first);
swap(l.second, r.second);
}
You meant defining the free function. I like your solution.
– Orient
Nov 23 '18 at 10:41
add a comment |
You can just include values in your range object.
struct Range { I first; I second; L & list; }
void swap(Range & l, Range & r)
{
assert(std::addressof(l.list) == std::addressof(r.list));
using std::swap;
auto ll = std::next(l.second);
auto rr = std::next(r.second);
if (ll == r.first) {
l.list.splice(rr, l.list, l.first, ll);
} else if (rr == l.first) {
l.list.splice(ll, l.list, r.first, rr);
} else {
L temp;
temp.splice(std::cend(temp), l.list, l.first, ll);
l.list.splice(ll, l.list, r.first, rr);
l.list.splice(rr, std::move(temp));
}
swap(l.first, r.first);
swap(l.second, r.second);
}
You meant defining the free function. I like your solution.
– Orient
Nov 23 '18 at 10:41
add a comment |
You can just include values in your range object.
struct Range { I first; I second; L & list; }
void swap(Range & l, Range & r)
{
assert(std::addressof(l.list) == std::addressof(r.list));
using std::swap;
auto ll = std::next(l.second);
auto rr = std::next(r.second);
if (ll == r.first) {
l.list.splice(rr, l.list, l.first, ll);
} else if (rr == l.first) {
l.list.splice(ll, l.list, r.first, rr);
} else {
L temp;
temp.splice(std::cend(temp), l.list, l.first, ll);
l.list.splice(ll, l.list, r.first, rr);
l.list.splice(rr, std::move(temp));
}
swap(l.first, r.first);
swap(l.second, r.second);
}
You can just include values in your range object.
struct Range { I first; I second; L & list; }
void swap(Range & l, Range & r)
{
assert(std::addressof(l.list) == std::addressof(r.list));
using std::swap;
auto ll = std::next(l.second);
auto rr = std::next(r.second);
if (ll == r.first) {
l.list.splice(rr, l.list, l.first, ll);
} else if (rr == l.first) {
l.list.splice(ll, l.list, r.first, rr);
} else {
L temp;
temp.splice(std::cend(temp), l.list, l.first, ll);
l.list.splice(ll, l.list, r.first, rr);
l.list.splice(rr, std::move(temp));
}
swap(l.first, r.first);
swap(l.second, r.second);
}
answered Nov 23 '18 at 10:38
CalethCaleth
17.3k22139
17.3k22139
You meant defining the free function. I like your solution.
– Orient
Nov 23 '18 at 10:41
add a comment |
You meant defining the free function. I like your solution.
– Orient
Nov 23 '18 at 10:41
You meant defining the free function. I like your solution.
– Orient
Nov 23 '18 at 10:41
You meant defining the free function. I like your solution.
– Orient
Nov 23 '18 at 10:41
add a comment |
I find a better way (if compare with ones based on swap function overloading) to implement desired: two-pass way to apply changes. It gives just linear overhead, instead of the algorithm of interest's complexity.
#include <utility>
#include <algorithm>
#include <vector>
#include <list>
#include <iostream>
#include <random>
#include <cassert>
inline
std::ostream & operator << (std::ostream & out, const std::pair< int, int > & p)
{
return out << '{' << p.first << ", " << p.second << '}';
}
int main()
{
using L = std::list< std::pair< int, int > >;
using I = typename L::const_iterator;
using P = std::pair< I, I >;
using R = std::vector< P >;
L values;
R ranges;
auto l = std::cbegin(values);
for (int i = 0; i < 10; ++i) {
l = values.emplace(std::cend(values), i, 0);
auto & p = ranges.emplace_back(l, l);
for (int j = 1; j <= i; ++j) {
p.second = values.emplace(std::cend(values), i, j);
}
}
for (const auto & p : values) {
std::cout << p << std::endl;
}
std::cout << "-----" << std::endl;
std::shuffle(std::begin(ranges), std::end(ranges), std::mt19937{std::random_device{}()});
l = std::cbegin(values);
for (const auto & range : ranges) {
auto r = std::next(range.second);
if (l != range.first) {
values.splice(l, values, range.first, r);
}
l = r;
}
assert(l == std::cend(values));
for (const auto & p : values) {
std::cout << p << std::endl;
}
}
Don't think it can be applicable to containers with somehow stricter iterators invalidation rules.
add a comment |
I find a better way (if compare with ones based on swap function overloading) to implement desired: two-pass way to apply changes. It gives just linear overhead, instead of the algorithm of interest's complexity.
#include <utility>
#include <algorithm>
#include <vector>
#include <list>
#include <iostream>
#include <random>
#include <cassert>
inline
std::ostream & operator << (std::ostream & out, const std::pair< int, int > & p)
{
return out << '{' << p.first << ", " << p.second << '}';
}
int main()
{
using L = std::list< std::pair< int, int > >;
using I = typename L::const_iterator;
using P = std::pair< I, I >;
using R = std::vector< P >;
L values;
R ranges;
auto l = std::cbegin(values);
for (int i = 0; i < 10; ++i) {
l = values.emplace(std::cend(values), i, 0);
auto & p = ranges.emplace_back(l, l);
for (int j = 1; j <= i; ++j) {
p.second = values.emplace(std::cend(values), i, j);
}
}
for (const auto & p : values) {
std::cout << p << std::endl;
}
std::cout << "-----" << std::endl;
std::shuffle(std::begin(ranges), std::end(ranges), std::mt19937{std::random_device{}()});
l = std::cbegin(values);
for (const auto & range : ranges) {
auto r = std::next(range.second);
if (l != range.first) {
values.splice(l, values, range.first, r);
}
l = r;
}
assert(l == std::cend(values));
for (const auto & p : values) {
std::cout << p << std::endl;
}
}
Don't think it can be applicable to containers with somehow stricter iterators invalidation rules.
add a comment |
I find a better way (if compare with ones based on swap function overloading) to implement desired: two-pass way to apply changes. It gives just linear overhead, instead of the algorithm of interest's complexity.
#include <utility>
#include <algorithm>
#include <vector>
#include <list>
#include <iostream>
#include <random>
#include <cassert>
inline
std::ostream & operator << (std::ostream & out, const std::pair< int, int > & p)
{
return out << '{' << p.first << ", " << p.second << '}';
}
int main()
{
using L = std::list< std::pair< int, int > >;
using I = typename L::const_iterator;
using P = std::pair< I, I >;
using R = std::vector< P >;
L values;
R ranges;
auto l = std::cbegin(values);
for (int i = 0; i < 10; ++i) {
l = values.emplace(std::cend(values), i, 0);
auto & p = ranges.emplace_back(l, l);
for (int j = 1; j <= i; ++j) {
p.second = values.emplace(std::cend(values), i, j);
}
}
for (const auto & p : values) {
std::cout << p << std::endl;
}
std::cout << "-----" << std::endl;
std::shuffle(std::begin(ranges), std::end(ranges), std::mt19937{std::random_device{}()});
l = std::cbegin(values);
for (const auto & range : ranges) {
auto r = std::next(range.second);
if (l != range.first) {
values.splice(l, values, range.first, r);
}
l = r;
}
assert(l == std::cend(values));
for (const auto & p : values) {
std::cout << p << std::endl;
}
}
Don't think it can be applicable to containers with somehow stricter iterators invalidation rules.
I find a better way (if compare with ones based on swap function overloading) to implement desired: two-pass way to apply changes. It gives just linear overhead, instead of the algorithm of interest's complexity.
#include <utility>
#include <algorithm>
#include <vector>
#include <list>
#include <iostream>
#include <random>
#include <cassert>
inline
std::ostream & operator << (std::ostream & out, const std::pair< int, int > & p)
{
return out << '{' << p.first << ", " << p.second << '}';
}
int main()
{
using L = std::list< std::pair< int, int > >;
using I = typename L::const_iterator;
using P = std::pair< I, I >;
using R = std::vector< P >;
L values;
R ranges;
auto l = std::cbegin(values);
for (int i = 0; i < 10; ++i) {
l = values.emplace(std::cend(values), i, 0);
auto & p = ranges.emplace_back(l, l);
for (int j = 1; j <= i; ++j) {
p.second = values.emplace(std::cend(values), i, j);
}
}
for (const auto & p : values) {
std::cout << p << std::endl;
}
std::cout << "-----" << std::endl;
std::shuffle(std::begin(ranges), std::end(ranges), std::mt19937{std::random_device{}()});
l = std::cbegin(values);
for (const auto & range : ranges) {
auto r = std::next(range.second);
if (l != range.first) {
values.splice(l, values, range.first, r);
}
l = r;
}
assert(l == std::cend(values));
for (const auto & p : values) {
std::cout << p << std::endl;
}
}
Don't think it can be applicable to containers with somehow stricter iterators invalidation rules.
edited Nov 23 '18 at 10:49
answered Nov 23 '18 at 10:43
OrientOrient
6,26153295
6,26153295
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53443555%2fhow-to-parametrize-an-algorithm-with-a-custom-non-pure-swap-function%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Rather than
rangesbeing a container ofstd::pair<I, I>, why not ofstruct { I first; I second; L & list; }?– Caleth
Nov 23 '18 at 9:17
As an aside, all your
p.seconds are elements of L of the formi, i. Do you intend that?– Caleth
Nov 23 '18 at 9:25
1
@Caleth It is one of thinkable solutions, but it leads to extra data duplication.
– Orient
Nov 23 '18 at 9:33
@Caleth
std::pair< int, int >elements are just for example.firstis just a number of a range here and thesecondis an unique value within the range. It is intended for ease of visual distinction. All ranges are of different lengths.– Orient
Nov 23 '18 at 9:40
For
struct { I first; I second; L * list = {}; }one also need to overload move constructor and move assignment operator to interoperate w/std::swapproperly.– Orient
Nov 23 '18 at 10:28