Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Higher order functions for the rest of us

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

Higher order functions for the rest of us

Higher order functions, i.e., functions that accept functions as arguments, or return functions, are a core part of functional programming. The C++ standard library also has an abundance of higher order functions, but it is rare for C++ developers to write their own.

I will show some simple types of higher order functions you can easily write yourself, that will reduce repetition, increase code clarity, and make you a more productive programmer.

Avatar for Björn Fahller

Björn Fahller

April 12, 2018

More Decks by Björn Fahller

Other Decks in Programming

Transcript

  1. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    1/41 compose([](auto const& s) { return s == "foo";}, std::mem_fn(&foo::name)) Higher order functions for the rest of us Björn Fahller
  2. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    2/41 Definition A higher-order function is a function that takes other functions as arguments or returns a function as result.
  3. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    3/41 #include <algorithm> #include <vector> std::vector<int> v; ... if (std::none_of(std::begin(v), std::end(v), [](int x) { return x == 0; })) { ... }
  4. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    4/41 #include <algorithm> #include <vector> std::vector<int> v; ... if (std::none_of(std::begin(v), std::end(v), [](int x) { return x == 0; })) { ... } template <typename Iterator, typename Predicate> bool none_of(Iterator i, Iterator e, Predicate predicate) { while (i != e) { if (predicate(*i)) return false; i++; } return true; }
  5. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    5/41 #include <algorithm> #include <vector> std::vector<int> v; ... if (std::none_of(std::begin(v), std::end(v), [](int x) { return x == 0; })) { ... } ... int num; ... while (std::any_of(std::begin(v), std::end(v), [num](int x){ return x == num; })) { ... }
  6. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    7/41 auto equals(int num) { return [num](int x){ return x == num; }; }
  7. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    8/41 template <typename T> auto equals(T num) { return [num](auto const& x){ return x == num; }; }
  8. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    9/41 #include <algorithm> #include <vector> std::vector<int> v; ... if (std::none_of(std::begin(v), std::end(v), equals(0)) { ... } ... int num; ... while (std::any_of(std::begin(v), std::end(v), equals(num)) { ... }
  9. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    10/41 struct ip { ip(uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4) : num((uint32_t(i1) << 24) | (uint32_t(i2) << 16) | (uint32_t(i3) << 8) | i4) {} ip(uint32_t n) : num(n) {} bool operator==(ip rh) const { return num == rh.num;} bool operator!=(ip rh) const { return !(*this == rh);} uint32_t num; }; struct netmask : ip { using ip::ip; }; inline ip operator&(ip lh, netmask rh) { return {lh.num & rh.num}; };
  10. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    11/41 struct ip { ip(uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4) : num((uint32_t(i1) << 24) | (uint32_t(i2) << 16) | (uint32_t(i3) << 8) | i4) {} ip(uint32_t n) : num(n) {} bool operator==(ip rh) const { return num == rh.num;} bool operator!=(ip rh) const { return !(*this == rh);} uint32_t num; }; struct netmask : ip { using ip::ip; }; inline ip operator&(ip lh, netmask rh) { return {lh.num & rh.num}; };
  11. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    12/41 struct ip { ip(uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4) : num((uint32_t(i1) << 24) | (uint32_t(i2) << 16) | (uint32_t(i3) << 8) | i4) {} ip(uint32_t n) : num(n) {} bool operator==(ip rh) const { return num == rh.num;} bool operator!=(ip rh) const { return !(*this == rh);} uint32_t num; }; struct netmask : ip { using ip::ip; }; inline ip operator&(ip lh, netmask rh) { return {lh.num & rh.num}; };
  12. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    13/41 struct ip { ip(uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4) : num((uint32_t(i1) << 24) | (uint32_t(i2) << 16) | (uint32_t(i3) << 8) | i4) {} ip(uint32_t n) : num(n) {} bool operator==(ip rh) const { return num == rh.num;} bool operator!=(ip rh) const { return !(*this == rh);} uint32_t num; }; struct netmask : ip { using ip::ip; }; inline ip operator&(ip lh, netmask rh) { return {lh.num & rh.num}; };
  13. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    14/41 struct ip { ip(uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4) : num((uint32_t(i1) << 24) | (uint32_t(i2) << 16) | (uint32_t(i3) << 8) | i4) {} ip(uint32_t n) : num(n) {} bool operator==(ip rh) const { return num == rh.num;} bool operator!=(ip rh) const { return !(*this == rh);} uint32_t num; }; struct netmask : ip { using ip::ip; }; inline ip operator&(ip lh, netmask rh) { return {lh.num & rh.num}; }; auto ip_matches(ip desired, netmask mask = netmask{255,255,255,255}) { return [desired, mask](ip actual) { return (desired & mask) == (actual & mask); }; }
  14. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    15/41 struct ip { ip(uint8_t i1, uint8_t i2, uint8_t i3, uint8_t i4) : num((uint32_t(i1) << 24) | (uint32_t(i2) << 16) | (uint32_t(i3) << 8) | i4) {} ip(uint32_t n) : num(n) {} bool operator==(ip rh) const { return num == rh.num;} bool operator!=(ip rh) const { return !(*this == rh);} uint32_t num; }; struct netmask : ip { using ip::ip; }; inline ip operator&(ip lh, netmask rh) { return {lh.num & rh.num}; }; auto ip_matches(ip desired, netmask mask = netmask{255,255,255,255}) { return [desired, mask](ip actual) { return (desired & mask) == (actual & mask); }; } std::vector<ip> v; ... auto i = std::remove_if(v.begin(), v.end(), ip_matches({192,168,1,1}, {255,255,0,0}));
  15. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    16/41 class ipif { public: using state_type = enum { off, on }; ... void set_state(state_type); state_type state() const { return state_; } ip addr() const { return addr_;} netmask mask() const { return mask_; } ip gw() const { return gw_; } private: ip addr_; netmask mask_; ip gw_; state_type state_; };
  16. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    17/41 class ipif { public: using state_type = enum { off, on }; ... void set_state(state_type); state_type state() const { return state_; } ip addr() const { return addr_;} netmask mask() const { return mask_; } ip gw() const { return gw_; } private: ip addr_; netmask mask_; ip gw_; state_type state_; };
  17. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    18/41 class ipif { public: using state_type = enum { off, on }; ... void set_state(state_type); state_type state() const { return state_; } ip addr() const { return addr_;} netmask mask() const { return mask_; } ip gw() const { return gw_; } private: ip addr_; netmask mask_; ip gw_; state_type state_; }; To match, for example the address of an ipif, we need to make the ip_matches() predicate work on a member.
  18. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    19/41 class ipif { public: using state_type = enum { off, on }; ... void set_state(state_type); state_type state() { return state_; } ip addr() const { return addr_;} netmask mask() const { return mask_; } ip gw() const { return gw_; } private: ip addr_; netmask mask_; ip gw_; state_type state_; }; Given: f1(y) -> z and f2(x) -> y We want a composition f(x)->z as f1(f2(x))
  19. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    20/41 class ipif { public: using state_type = enum { off, on }; ... void set_state(state_type); state_type state() { return state_; } ip addr() const { return addr_;} netmask mask() const { return mask_; } ip gw() const { return gw_; } private: ip addr_; netmask mask_; ip gw_; state_type state_; }; Given: f1(y) -> z ip_matches(ip) -> bool and f2(x) -> y select_addr(ipif) -> ip We want a composition f(x)->z as f1(f2(x))
  20. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    21/41 class ipif { public: using state_type = enum { off, on }; ... void set_state(state_type); state_type state() { return state_; } ip addr() const { return addr_;} netmask mask() const { return mask_; } ip gw() const { return gw_; } private: ip addr_; netmask mask_; ip gw_; state_type state_; }; template <typename F1, typename F2> auto compose(F1 f1, F2 f2) { return [=](const auto& x) { return f1(f2(x)); }; } Given: f1(y) -> z ip_matches(ip) -> bool and f2(x) -> y select_addr(ipif) -> ip We want a composition f(x)->z as f1(f2(x))
  21. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    22/41 class ipif { public: ... ip addr() const { return addr_;} ... private: ip addr; ... }; std::vector<ipif> interfaces; auto i = std::find_if(interfaces.begin(), interfaces.end(), compose(ip_matches({192,168,1,1}), select_addr));
  22. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    23/41 class ipif { public: ... ip addr() const { return addr_;} ... private: ip addr; ... }; ip select_addr(ipif const& interface) { return interface.addr(); } std::vector<ipif> interfaces; auto i = std::find_if(interfaces.begin(), interfaces.end(), compose(ip_matches({192,168,1,1}), select_addr));
  23. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    24/41 class ipif { public: ... ip addr() const { return addr_;} ... private: ip addr; ... }; auto addr_matches(ip addr, netmask mask = {255,255,255,255}) { return compose(ip_matches(addr, mask), select_addr); } std::vector<ipif> interfaces; auto i = std::find_if(interfaces.begin(), interfaces.end(), compose(ip_matches({192,168,1,1}), select_addr);
  24. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    25/41 class ipif { public: ... ip addr() const { return addr_;} ... private: ip addr; ... }; auto addr_matches(ip addr, netmask mask = {255,255,255,255}) { return compose(ip_matches(addr, mask), select_addr); } std::vector<ipif> interfaces; auto i = std::find_if(interfaces.begin(), interfaces.end(), addr_matches({192,168,1,1}));
  25. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    26/41 class ipif { public: using state_type = enum { off, on }; ... void set_state(state_type); state_type state() { return state_; } ip addr() const { return addr_;} netmask mask() const { return mask_; } ip gw() const { return gw_; } ... }; inline ip select_gw(ipif const& interface) { return interface.gw(); } inline ipif::state_type select_state(ipif const& interface) { return interface.state(); }
  26. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    27/41 auto i = find_if(v.begin(), v.end(), when_all(addr_matches({192,168,1,1},{255,255,0,0}), state_is(ipif::off)));
  27. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    28/41 template <typename ... Predicates> auto when_all(Predicates ... ps) { return [=](auto const& x) { return (ps(x) && ...); }; } auto i = find_if(v.begin(), v.end(), when_all(addr_matches({192,168,1,1},{255,255,0,0}), state_is(ipif::off)));
  28. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    29/41 template <typename ... Predicates> auto when_all(Predicates ... ps) { return [=](auto const& x) { return (ps(x) && ...); }; } auto addr_matches(ip addr, netmask mask=netmask{255,255,255,255}) { return compose(match_ip(addr, mask), select_addr); } auto i = find_if(v.begin(), v.end(), when_all(addr_matches({192,168,1,1},{255,255,0,0}), state_is(ipif::off)));
  29. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    30/41 template <typename ... Predicates> auto when_all(Predicates ... ps) { return [=](auto const& x) { return (ps(x) && ...); }; } auto addr_matches(ip addr, netmask mask=netmask{255,255,255,255}) { return compose(match_ip(addr, mask), select_addr); } auto state_is(ipif::state_type state) { return compose(equals(state), select_state); } auto i = find_if(v.begin(), v.end(), when_all(addr_matches({192,168,1,1},{255,255,0,0}), state_is(ipif::off)));
  30. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    31/41 for_each(v.begin(), v.end(), if_then(when_all(addr_matches({192,168,1,1}, {255,255,0,0}), state_is(ipif::off)), set_state(ipif::on)));
  31. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    32/41 template <typename Predicate, typename Action> auto if_then(Predicate predicate, Action action) { return [=](auto&& x) { if (predicate(x)) { action(std::forward<decltype(x)>(x)); } }; } for_each(v.begin(), v.end(), if_then(when_all(addr_matches({192,168,1,1}, {255,255,0,0}), state_is(ipif::off)), set_state(ipif::on)));
  32. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    33/41 template <typename Predicate, typename Action> auto if_then(Predicate predicate, Action action) { return [=](auto&& x) { if (predicate(x)) { action(std::forward<decltype(x)>(x)); } }; } auto set_state(ipif::state_type state) { return [=](ipif& interface) { interface.set_state(state); }; } for_each(v.begin(), v.end(), if_then(when_all(addr_matches({192,168,1,1}, {255,255,0,0}), state_is(ipif::off)), set_state(ipif::on)));
  33. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    35/41 Generic library functions • equals(value) ... • compose(function...) • if_then(pred,action) • when_all(predicate...) • when_none(predicate...) • when_any(predicate...) • do_all(action...) Domain specific functions • ip_matches(ip, mask) • select_addr(ipif) • select_gw(ipif) • select_state(ipif) • set_state(ipif&) Composed domain specific functions • addr_matches(ip) • state_is(state_type)
  34. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    36/41 https://github.com/rollbear/lift Generic library functions • equals(value) ... • compose(function...) • if_then(pred,action) • when_all(predicate...) • when_none(predicate...) • when_any(predicate...) • do_all(action...) Domain specific functions • ip_matches(ip, mask) • select_addr(ipif) • select_gw(ipif) • select_state(ipif) • set_state(ipif&) Composed domain specific functions • addr_matches(ip) • state_is(state_type)
  35. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    37/41 Take away messages • Write functions that uses auto return type to create lambdas
  36. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    38/41 Take away messages • Write functions that uses auto return type to create lambdas • Write functions that access or modify your state
  37. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    39/41 Take away messages • Write functions that uses auto return type to create lambdas • Write functions that access or modify your state • Compose functions
  38. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    40/41 Take away messages • Write functions that uses auto return type to create lambdas • Write functions that access or modify your state • Compose functions • And give names to the compositions
  39. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller

    41/41 Björn Fahller https://github.com/rollbear/lift [email protected] @bjorn_fahller @rollbear cpplang, swedencpp Higher order functions for the rest of us