3 ms·
> Interestingly enough, most of these implementations only flip the first two parameters of whichever function they are passed, though it seems to be because mo
by zahlman 1y ago
> Interestingly enough, most of these implementations only flip the first two parameters of whichever function they are passed, though it seems to be because most of them are based on the Haskell prelude, and handling arbitrary arity can be tricky in that language.
Probably because the use case for it with higher arity is hard to imagine. (Indeed, TFA gives only examples with binary operations.)
> Fortunately it is not just useless knowledge either: flip can be reified at will by copying the following C++17 implementation.
> [snip 114 lines of code]
Meanwhile, in Python:
def flip(f):
return lambda *args, **kwargs: f(*args[::-1], **kwargs)
(I leave keyword arguments alone because there's no clearer semantic for "flipping" them.)
The `toolz.functoolz.flip` implementation (being restricted to binary functions) is an even simpler one-liner (https://toolz.readthedocs.io/en/latest/_modules/toolz/functoolz.html#flip https://toolz.readthedocs.io/en/latest/_modules/toolz/functo...), though accompanied by a massive docstring and admittedly simplified through a heavyweight currying decorator (which accomplishes much more than simply getting a function that does the right thing).
- Jaxan 1y agoFor higher arity there is a combinatorial explosion of all the possible permutations. But if you want to flip the 2nd and 3rd argument in Haskell it can be done by flip itself: flip23 foo = (\x -> flip (foo x))
- marvinborner 1y agoOr just (flip .), which also allows ((flip .) .) etc. for further flips. In Smullyan's "To Mock a Mockingbird", these combinators are described as "cardinal combinator once/twice/etc. removed", where the cardinal combinator itself defines flip.
- Jaxan 1y agoI was hoping someone would provide something more succinct ;-). Thanks!
- Matheus28 1y agoYour python code allocates an array and inverts it every function call. The C++ code has no overhead and is equivalent to a compile time transformation.
- zahlman 1y agoOf course. But if I had to care about things on that level, and I was willing to sit through the C++ compilation process (and everything else that goes along with that), I wouldn't be using Python in the first place.
- GrantMoyer 1y agoThere's a somewhat easier way to implement 2-argument-function flip in C++ than the blog post provides: #include <functional> constexpr auto flip(const auto& f) { return std::bind(f, std::placeholders::_2, std::placeholders::_1); } The best I could get the fully general version is still pretty obtuse though: // std doesn't have a template version of placeholders::_1, _2, etc., so we need to // define our own. template <int I> struct placeholder{}; template<> template<int I> struct std::is_placeholder<placeholder<I>> : std::integral_constant<int, I> {}; // flip must be an object so that the function type can be deduced without needing // to explicitly specify its parameters' types. template<typename F> struct flip { const F f; // operator() deduces the argument types when the flip object is called, but really // all we need to know is the number of arguments. template<typename... Args> constexpr auto operator()(Args... args) { return bind_reversed(std::make_integer_sequence<int, sizeof...(Args)>{})(args...); } private: // a helper function is needed to deduce a sequence of integers so we can bind all // the placeholder values. template<int... Is> constexpr auto bind_reversed(std::integer_sequence<int, Is...>) { return std::bind(f, placeholder<sizeof...(Is) - Is>{}...); } };