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

Higher order functions for the rest of us

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.

Björn Fahller

April 12, 2018
Tweet

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

    View Slide

  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.

    View Slide

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

    View Slide

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

    View Slide

  5. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller 5/41
    #include
    #include
    std::vector 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; })) {
    ...
    }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  9. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller 9/41
    #include
    #include
    std::vector 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)) {
    ...
    }

    View Slide

  10. 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};
    };

    View Slide

  11. 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};
    };

    View Slide

  12. 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};
    };

    View Slide

  13. 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};
    };

    View Slide

  14. 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);
    };
    }

    View Slide

  15. 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 v;
    ...
    auto i = std::remove_if(v.begin(), v.end(),
    ip_matches({192,168,1,1}, {255,255,0,0}));

    View Slide

  16. 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_;
    };

    View Slide

  17. 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_;
    };

    View Slide

  18. 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.

    View Slide

  19. 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))

    View Slide

  20. 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))

    View Slide

  21. 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
    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))

    View Slide

  22. 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 interfaces;
    auto i = std::find_if(interfaces.begin(), interfaces.end(),
    compose(ip_matches({192,168,1,1}),
    select_addr));

    View Slide

  23. 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 interfaces;
    auto i = std::find_if(interfaces.begin(), interfaces.end(),
    compose(ip_matches({192,168,1,1}),
    select_addr));

    View Slide

  24. 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 interfaces;
    auto i = std::find_if(interfaces.begin(), interfaces.end(),
    compose(ip_matches({192,168,1,1}),
    select_addr);

    View Slide

  25. 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 interfaces;
    auto i = std::find_if(interfaces.begin(), interfaces.end(),
    addr_matches({192,168,1,1}));

    View Slide

  26. 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();
    }

    View Slide

  27. 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)));

    View Slide

  28. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller 28/41
    template
    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)));

    View Slide

  29. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller 29/41
    template
    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)));

    View Slide

  30. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller 30/41
    template
    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)));

    View Slide

  31. 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)));

    View Slide

  32. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller 32/41
    template
    auto if_then(Predicate predicate, Action action)
    {
    return [=](auto&& x)
    {
    if (predicate(x)) {
    action(std::forward(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)));

    View Slide

  33. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller 33/41
    template
    auto if_then(Predicate predicate, Action action)
    {
    return [=](auto&& x)
    {
    if (predicate(x)) {
    action(std::forward(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)));

    View Slide

  34. Higher Order Functions – ACCU 2018 © Björn Fahller @bjorn_fahller 34/41
    Live demo!

    View Slide

  35. 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)

    View Slide

  36. 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)

    View Slide

  37. 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

    View Slide

  38. 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

    View Slide

  39. 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

    View Slide

  40. 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

    View Slide

  41. 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

    View Slide