Higher-Order functions in C++

The other day, I was writing some C++ and found that I was thinking about how to manipulate the data I had as if I was writing F#. It would have been convenient to turn a std::map into an array of tuples, which I could do in F# like this:

let f (xs : map<int,string) =
  let xs = xs |> Map.toArray
  // now treat xs as array of tuples...

There’s no function in STL to do this off the bat – instead, you have to roll your own (not that it’s much code, but it does break your through processes if you have to stop to code this sort of thing every time).

Of course, this is just one of many helpful F# higher-order functions that are provided in the F# Map module – and there are counterparts for each of the collection classes i.e. Array, Set, List etc. In C++, the nearest equivalent is the STL which provides both collection classes and a number of algorithms that operate on them. Better still, from C++11 onwards we have lambdas, which make using STL algorithms much easier. Even so, in most cases, the F# operations seem much more tailored to the sort of data transformation I see at work – our codebase is littered with map/filter/fold operations as people transform/select and accumulate data. Conversely, our C++ codebase is full of … for loops, evidence in my eyes that STL algorithms aren’t as immediately applicable. In fact, the ease of use of higher-order functions was one of the reasons that F# was quickly adopted in my workplace (along with immutability, strong-typing, conciseness, type inference and syntax checking).

I’ve written one-to-one C++ equivalents of the F# module functions that I use the most for Map and Vector – see below. Interestingly, I found that I really did have to ‘engage brain’ to write some of these, particularly Map.filter. For that one, you can’t use the erase-remove idiom because map keys are both const and strictly ordered (whereas for Vector, erase-remove_if implements filter neatly). A library of functions as per my code below would definitely be a productivity boost.

First, I’ve factored some common utilities into namespace Collection:

namespace MusingStudio
{
    namespace Collection
    {
        template <typename C, typename F>
        C& filter( C& collection, F keep_predicate )
        {
            auto erase_predicate = [&pred=keep_predicate]( auto&& x ){ return !pred( std::forward<decltype(x)>(x) ); };
            collection.erase( std::remove_if( collection.begin(), collection.end(), erase_predicate ), collection.end() );
            return collection;
        }
        
        // This form of filter always takes a copy and applies the filter to it
        // - sometimes you want to preserve the original collection
        template <typename C, typename F>
        C filter_copy( const C& collection, F keep_predicate )
        {
            C target;
            std::copy_if( collection.begin(), collection.end(), std::inserter( target, target.end() ), keep_predicate );
            return target;
        }
        
        template <typename C, typename F, typename A>
        A fold( const C& items, F f, A&& init )
        {
            A acc{ std::forward<A>(init) };
            for ( const auto& item : items )
            {
                f( acc, item );
            }
            return acc;
        }
                
        // F( T ) -> T and collection C is mutated
        template <typename C, typename F>
        C& transform( C& items, F f )
        {
            for ( auto& t : items )
            {
                t = f( t );
            }
            
            return items;
        }

    }
}

Next, here are the higher-order functions that I use for Map:

namespace MusingStudio
{
    namespace Map
    {
        // filter_copy takes a copy of the original collection then applies the filter
        template< typename K, typename V, typename F>
        std::map<K,V> filter_copy( const std::map<K,V>& items, F predicate )
        {
            return Collection::filter_copy( items, predicate );
        }
        
        template< typename K, typename V, typename F>
        std::map<K,V>& filter( std::map<K,V>& items, F predicate )
        {
            // NB the erase-remove_if idiom does not work for std::map
            // because the nodes must remain ordered by key.  This is enforced
            // by std::map<K,V> holding keys as const K.  So any assignment
            // to the key (to effecfively re-order the binary tree) fails to compile.
            // http://stackoverflow.com/questions/9515357/map-lambda-remove-if
            
            // instead, manually iterate over the collection, erasing items
            // for which predicate() returns false
            for ( auto it = items.begin(), itEnd = items.end(); it != itEnd; )
            {
                if ( predicate( *it ) )
                {
                    ++it; // ok - keep this item
                }
                else
                {
                    it = items.erase( it );
                }
            }
            
            return items;
        }
        
        template <typename K, typename V>
        auto to_vector( const std::map<K,V>& collection )
        {
            std::vector< std::pair<K,V> > items;
            
            for ( const auto& item : collection )
            {
                items.push_back( std::make_pair( item.first, item.second ) );
            }
            
            return items;
        }
        
        template <typename K, typename V>
        auto keys( const std::map<K,V>& collection )
        {
            std::set< K > items;
            
            for ( const auto& item : collection )
            {
                items.insert( item.first );
            }
            
            return items;
        }
        
        template <typename K, typename V>
        auto values( const std::map<K,V>& collection )
        {
            std::vector< V > items;
            
            for ( const auto& item : collection )
            {
                items.push_back( item.second );
            }
            
            return items;
        }
        
        template<typename K, typename V, typename F, typename A>
        A fold( const std::map<K,V>& items, F f, A&& init )
        {
            return Collection::fold( items, f, std::forward<A>(init) );
        }
        
        // F( std::pair<K,V> ) -> std::pair< L, U >
        // Construct a new std::map<L,U> mapping from (K,V) to (L,U)
        template <typename K, typename V, typename F>
        auto map( const std::map<K,V>& items, F f )
        {
            using KVP = typename std::map<K,V>::value_type;
            using RVP = decltype( f( KVP() ) );
            
            std::map< decltype( RVP().first ), decltype( RVP().second ) > result;
            
            for ( const KVP& kvp : items )
            {
                result.insert( f( kvp ) );
            }
            
            return result;
        }
        
        // F( K, V ) -> V and std::map<K,V> is mutated
        template <typename K, typename V, typename F>
        std::map<K,V>& transform( std::map<K,V>& items, F f )
        {
            using KVP = typename std::map<K,V>::value_type;
            
            for ( const KVP& kvp : items )
            {
                items[kvp.first] = f( kvp.first, kvp.second );
            }
            
            return items;
        }
    }
}

And here are the higher-order functions that I use for Vector:

namespace MusingStudio
{
    namespace Vector
    {
        template< typename T, typename F>
        std::vector<T>& filter( std::vector<T>& items, F predicate )
        {
            Collection::filter( items, predicate );
            return items;
        }
        
        template< typename T, typename F>
        std::vector<T> filter_copy( const std::vector<T>& items, F predicate )
        {
            return Collection::filter_copy( items, predicate );
        }
        
        // Requires F to have signature void( A&, T )
        template< typename T, typename F, typename A>
        A fold( const std::vector<T>& items, F f, A&& init )
        {
            return Collection::fold( items, f, std::forward<A>(init) );
        }
        
        template< typename T, typename P = std::less<T> >
        std::vector<T>& sort( std::vector<T>& items, P compare = P() )
        {
            std::sort( items.begin(), items.end(), compare );
            return items;
        }
        
        template< typename T, typename P = std::less<T> >
        std::vector<T> sort_copy( const std::vector<T>& items, P compare = P() )
        {
            std::vector<T> result( items );
            std::sort( result.begin(), result.end(), compare );
            return result;
        }
        
        // F( T ) -> U, construct a new vector<U>, mapping from T to U
        template <typename T, typename F>
        auto map( const std::vector<T>& items, F f )
        {
            using U = decltype( f(T()) );
            
            std::vector< U > result;
            std::transform( items.begin(), items.end(), std::inserter( result, result.end() ), f );
            return result;
        }
        
        // F( T ) -> T and std::vector<T> is mutated
        template <typename T, typename F>
        std::vector<T>& transform( std::vector<T>& items, F f )
        {
            return Collection::transform( items, f );
        }
    }
}

Here are some unit tests that show how much easier it is to use the Map/Vector functions instead of going directly to STL – I’d argue that this code is comparable to F# for conciseness (although F# code would still benefit from pipelining subsequent operations).

#include <iostream>

#include <gmock/gmock.h>
#include <Vector.hpp>
#include <Map.hpp>

using namespace testing;
using namespace MusingStudio;

TEST( Map, to_vector )
{
    using Mapped = std::map<int, std::string>;
    using Tuples = std::vector<std::pair<int,std::string> >;
    
    Mapped items{ { 1, "Hi" }, { 2, "Bye" } };
    
    EXPECT_EQ( (Tuples{ { 1, "Hi" }, { 2, "Bye" } }), 
      Map::to_vector( items ) );
}

TEST( Map, keys )
{
    using Mapped = std::map<int, std::string>;
    using Keys = std::set<int>;
    
    Mapped items{ { 1, "Hi" }, { 2, "Bye" } };
    
    EXPECT_EQ( (Keys{ 1, 2 }), 
      Map::keys( items ) );
}

TEST( Map, values )
{
    using Mapped = std::map<int, std::string>;
    using Values = std::vector<std::string>;
    
    Mapped items{ { 1, "Hi" }, { 2, "Bye" } };
    
    EXPECT_EQ( (Values{ "Hi", "Bye" }), 
      Map::values( items ) );
}

TEST( Map, filter )
{
    using Mapped = std::map<int, int>;
    
    Mapped items{ {1,1}, {2,4}, {3,9}, {4,16} };
    
    Mapped even_keys{ {2,4},{4,16} };
    auto lambda = []( const auto& keyvaluepair ){ return keyvaluepair.first % 2 == 0; };
    
    // Map::filter will mutate parameter 'items'
    EXPECT_EQ( even_keys, 
      Map::filter( items, lambda ) );
    EXPECT_EQ( 2, items.size() );
}

TEST( Map, filter_copy )
{
    using Mapped = std::map<int, int>;
    
    Mapped items{ {1,1}, {2,4}, {3,9}, {4,16} };
    
    Mapped even_keys{ {2,4},{4,16} };
    auto lambda = []( const auto& keyvaluepair ){ return keyvaluepair.first % 2 == 0; };
    
    // Map::filter_copy creates a copy, so parameter 'items' is untouched
    EXPECT_EQ( even_keys, 
      Map::filter_copy( items, lambda ) );
    EXPECT_EQ( 4, items.size() );
}

TEST( Map, fold )
{
    using Mapped = std::map<int, int>;
    
    Mapped items{ {1,2}, {3,4}, {5,6} };
    
    // Map::fold takes F( A&, pair<K,V> ) -> void
    EXPECT_EQ( 21, 
      Map::fold( items, 
        []( int& acc, const auto& kvp ){ acc += kvp.first + kvp.second; }, 0 ) );
}

TEST( Map, transform_mutable_values_only )
{
    using Transformed = std::map<int, int>;

    // Map::transform over the values, mutating them
    // Takes F(K,V) -> V i.e. the type of the return value must be V
    // "items" must be a named variable because parameter is non-const
    // (we will mutate it)
    Transformed items = { {1,1}, {2,2}, {3,3} };
    
    EXPECT_EQ( (Transformed{ {1,1}, {2,4}, {3,9} }),
      Map::transform( items, 
        []( int _, int v ){ return v*v; } ) );
}

TEST( Map, map_keys_and_values )
{
    using Mapped = std::map<int,double>;
    
    // Map::map over the pairs<key,value>
    // Takes F( pair<K,V> ) -> pair<K',V'> 
    // i.e. both key and value types can change
    auto lambda =
        []( const auto& kvp )
        {
            return std::make_pair( kvp.first + kvp.second,
                                   (double)kvp.second / (double)kvp.first );
        };
    
    // Map::map - new keys and values, not mutating the original collection, 
    // can be passed as unnamed temporary
    EXPECT_EQ( (Mapped{ {2,1}, {6,2} }),
      Map::map( std::map<int,int>{ {1,1}, {2,4} }, lambda ) );
}

TEST( Vector, filter )
{
    std::vector<int> items{ 1,2,3,4,5,4,3,2,1 };

    // Vector::filter will mutate the input collection
    EXPECT_EQ( (std::vector<int>{1,2,2,1}),
      Vector::filter( items,
        [](int i){ return 0 <= i && i <= 2; } ) );
    EXPECT_EQ( 4, items.size() );
}

TEST( Vector, filter_copy )
{
    std::vector<int> items{ 1,2,3,4,5,4,3,2,1 };
    auto untouched_size = items.size();
    
    // Vector::filter_copy creates a copy, so parameter 'items' is untouched
    EXPECT_EQ( (std::vector<int>{1,2,2,1}),
      Vector::filter_copy( items,
        [](int i){ return 0 <= i && i <= 2; } ) );
    EXPECT_EQ( untouched_size, items.size() );
}

TEST( Vector, fold )
{
    std::vector<int> items{ 1, 2, 3, 4, 5, -4, -6, -2, -1 };
    // Vector::fold takes F( A&, T ) -> void
    auto accumulate_squares = []( std::set<int>& acc, int i ){ acc.insert(i*i); };
    std::set<int> expected{1, 4, 9, 16, 25, 36};
    EXPECT_EQ( expected, 
      Vector::fold( items, accumulate_squares, std::set<int>{} ) );
}

TEST( Vector, sort )
{
    // Vector::sort mutates the input, hence input is non-const reference
    std::vector<int> items{1,2,1,3};
    EXPECT_EQ( (std::vector<int>{1,1,2,3}), 
      Vector::sort( items ) );
}

TEST( Vector, sort_copy )
{
    // Vector::sort_copy copies the input collection,
    // so collection parameter is const& (and can be an unnamed temporary)
    EXPECT_EQ( (std::vector<int>{1,1,2,3}), 
      Vector::sort_copy( std::vector<int>{1,2,1,3} ) );
}

TEST( Vector, map )
{
    // Vector::map takes F(T) -> U
    // Input collection is const and a new collection is returned
    EXPECT_EQ( (std::vector<double>{ 1.1, 2.1, 3.1 }),
      Vector::map( std::vector<int>{ 1,2,3 },
        []( int i ){ return (double)i + 0.1; } ) );
}

TEST( Vector, transform )
{
    // Vector::transform takes F(T) -> T
    // Input collection is mutated
    std::vector<int> items{ 1, 2, 3 };
    EXPECT_EQ( (std::vector<int>{ 2, 4, 6 }),
      Vector::transform( items, []( int i ){ return 2*i; } ) );
}

int main(int argc, char* argv[]) 
{    
    InitGoogleMock( &argc, argv );
    return RUN_ALL_TESTS();   
}

Notice that we can bypass immutability in C++, so whereas in F# Map::filter would always create a copy, it could be preferable in C++ to filter in-place. With that in mind, I’ve written both filter and filter_copy variations. There’s a similar dilemma for map operations – if you want free rein over the output types, then use Map::map or Vector::map. But if you want to transform the data in place (sticking to the existing types), use Map::transform or Vector::transform.

That covers the most popular functions for just Map and Vector, but it would be straight-forward to extend the library to cover List, Set and others. Similarly, I’d like to extend it to include higher-order functions like Choose, but I’ll need C++17’s std::optional for that.

2 Comments

Filed under C++, C++ Code, Programming

Restaurant Review: Fenchurch 37, Sky Garden, London

One of the attractions of dining at Fenchurch Restaurant is that, being on the 37th floor of the Walkie Talkie building in London, there are fine views across the capital. You’re free to enjoy the Sky Garden part of the complex on floors 35 & 36 before heading up to the restaurant for dinner.
fenchurch-restaurant
The restaurant itself was classy, yet the atmosphere was relaxed, with very helpful staff. We chose the Rabbit Bolognaise, followed by Loin and sausage of Venison. Both were excellent – a flawless meal, with excellent service and the novelty of views across city.
skygardenview
Five Stars

Leave a comment

Filed under Restaurant Review

Book Review: The Burning Room, Michael Connelly

theburningroomThis is another in the Harry Bosch series. He teams up with a rookie detective Lucia Soto to investigate the death of a mariachi band member (who succumbed to complications from being shot ten years earlier). We later discover that Soto has connections to the area and a background investigation runs in parallel into a horrific fire that killed a number of children (and from which Soto escaped).
Three Stars

Leave a comment

Filed under Book Review

Book Review: Cities In Flight, James Blish

cities-in-flightThis is another in the SF Masterworks series and I’m not alone in thinking it’s brilliant:

Exciting, intelligent galaxy-spanning stuff that these days would require six brick-thick volumes. This is the real heady wine of science fiction – Terry Pratchett

.

The story starts with two inventions – spindizzies (kind of anti-gravity engines) and anti-agathic drugs (that enable citizens to live for a thousand years) – and takes the reader on a journey to explore their exploitation. On the way, we encounter vast experimental stations on Jupiter, cities taking flight from earth to explore the galaxy, the economic collapse of the galaxy and even the end of time itself.

20141118-205616.jpg

Leave a comment

Filed under Book Review

Stateful Lambdas (Jason Turner)

I just watched Jason Turner’s latest C++ Weekly video, where he uses C++14 generalised lambda capture to implement the fibonacci sequence:

screen-shot-2016-11-15-at-13-54-29

This is very cool – I hadn’t seen Fibonacci done this way before. I was interested in std::exchange too, introduced in C++14. As used here, std::exchange replaces the existing value of j with a new value and returns the original value, which is assigned to i. So this idiom allows you to update a value and store the previous value, all in a single expression.

Even better, Jason shows that this whole program is calculated at compile time in Clang – very impressive.

Leave a comment

Filed under C++

How to Learn Python II

python-logoI recently wrote about starting to learn Python using HackerRank exercises. I’ve also been recommended Paul Ross’s training exercises. I think you need to have covered some introduction to the language first of all, but these exercises are accompanied by useful tips and solutions, which is very helpful. I also downloaded PyTest as per the recommendations – it’s easy to install (just download the zip from github and run “python setup.py install” as an admin) and provides neat unit testing for Python applications.

2 Comments

Filed under Programming, Python

IET Meetup – Sir Henry Royce Memorial Lecture 2016

meetup-henry-royce-lectureThis evening’s lecture at the IET was given by Chris Aylett of the Motorsport Industry Association. Chris gave a fast-paced overview of the work of motorsport engineers within their own industry and the increasing crossover into other sectors. He is a fan of horizontal innovation, the application of under-used skills and capacity within a firm to satisfy demand from clients in other industries.

This is particularly appropriate for the world-class unique capabilities of R&D-based motorsport suppliers in the UK who are able to resolve disparate engineering problems, and do so very quickly.

Particular examples were given by speakers from Wirth Research, Prodrive and Lentus Composites. The latter were responsible for the design of the Team GB track bikes which did rather well at the Rio Olympics – having been developed in just 13 months.

There was also plenty to reference from the inspirational life story of Sir Henry Royce. Despite having only one year of formal schooling, he became an apprentice engineer and ultimately started his own business making cranes. Not only did he expand into making motor cars and design the first aero-engine to fly over 400mph (which was developed into the famous Rolls-Royce Merlin engine in WWII Spitfires) – he also designed the bayonet lightbulb.

Leave a comment

Filed under Meetup

How to calculate Fibonacci sequence for large numbers

Firstly, here’s a basic implementation of Fibonacci using recursion:

    using Numbers = std::vector<unsigned long long>;
    Numbers cache( 5000, 0 );
    
    unsigned long long fibonacciRecursiveImpl( size_t n, Numbers& cache )
    {
        if ( n == 0 ) return 0ULL;
        if ( n == 1 ) return 1ULL;
        
        if ( cache[n] != 0 ) return cache[n];
        
        auto num = fibonacciRecursiveImpl( n-1, cache ) + fibonacciRecursiveImpl( n-2, cache );
        cache[n] = num;
        return num;
    }
    
    unsigned long long fibonacciRecursive( size_t n )
    {
        return fibonacciRecursiveImpl( n, cache );
    }

This works fine for small numbers (e.g. up to 20). I was interested to know where it would fail first, data overflow (due to the size of the numbers involved) or stack overflow (due to the recursive approach)? I implemented this on a MacBook using Apple LLVM 7.0 and the C++14 flag, but no other special switches set. It turns out that the overflow problems kick in for n > 93, but there was no sign of stack overflows, even up for n ~ 2000.

Even if there was a stack overflow, you could still use recursion, but change to a tail recursive solution (many C++ compilers will eliminate tail calls, essentially turning this into an iterative solution):

    // Implement recursive approach with possibility for "Tail call elimination",
    // avoids any concerns about stack overflow
    unsigned long long fibonacciTailRecursiveImpl( size_t n, unsigned long long prevprev, unsigned long long prev )
    {
        if ( n == 0 ) return prevprev;
        if ( n == 1 ) return prev;
        
        return fibonacciTailRecursiveImpl( n-1, prev, prevprev + prev );
    }
    
    unsigned long long fibonacciTailRecursive( size_t n )
    {
        return fibonacciTailRecursiveImpl( n, 0, 1 );
    }

So how to avoid the data overflow for Fibonacci over n = 93? At that point, you need to introduce a large number type with its own internal representation of large integers and suitable operator+ implementation. I used one such, BigNum, for this HackerRank challenge. The implementation stores the large integer as a string, with each character taking values in the range [0,100]. This reduces the number of digit-by-digit additions by a factor of 10 compared to a straight decimal representation.

I replaced unsigned long long with BigNum in the recursive solution above and verified that it returns the correct answers for n ~ 2000, with no stack overflows. Here, I’ll show it in an iterative solution (if you don’t want to keep a cache around, this is highly memory efficient, because you only need to store the previous two values):

    BigNum fibonacciBigNumIterativeImpl( size_t n )
    {
        BigNum prevprev(0);
        BigNum prev(1);
        
        if ( n == 0 ) return prevprev;
        if ( n == 1 ) return prev;
        
        BigNum num(0);
        for ( size_t i = 2; i <= n; ++i )
        {
            num = prevprev + prev;
            prevprev = prev;
            prev = num;
        }
        return num;
    }
    
    std::string fibonacciBigNumIterative( size_t n )
    {
        auto result = fibonacciBigNumIterativeImpl( n );
        return result.toString();
    }

Leave a comment

Filed under C++, C++ Code, Programming

How to approximate Pi using C++11 random number generators

The other day I learnt a method to approximate Pi if you have a random number generator for the range [0,1]. Consider a unit circle centred on (0,0) in 2D coordinates. Then one quarter of the area of the circle lies in the quadrant formed by x and y in range [0,1]. The area of the quarter circle is Pi * R^2/4, and here, R = 1 (it’s a unit circle). So we can generate a bunch of random 2D points, calculate the ratio between those that fall inside the circle and those in the outer unit square, then multiply by 4 to approximate Pi.
approximatepi
That sounds like a neat test case for the C++11 random number generators, so I thought I’d try it out. It turns out to work pretty well, if you’re prepared to use a sufficiently large number of random values.

    double approxPi( size_t points )
    {
        std::random_device rand_device;
        
        // mersenne_twister_engine is a random number engine 
        // based on Mersenne Twister algorithm.
        std::mt19937 generator( rand_device() );
        
        // We want random values uniformly distributed in [0,1]
        std::uniform_real_distribution<> unif_zero_one(0, 1);
        
        size_t points_inside{0};
        
        for (int i = 0; i < points; ++i )
        {
            auto x = unif_zero_one( generator );
            auto y = unif_zero_one( generator );
            double d = std::sqrt( x*x + y*y );
            
            if ( d <= 1.0 )
                ++points_inside;
        }
                
        return 4.0 * (static_cast<double>(points_inside) / static_cast<double>(points));
    }
}

void testApproximatePi()
{
    SHOULD_BE_APPROX( 3.14159, 0.3, approxPi( 100 ) );
    SHOULD_BE_APPROX( 3.14159, 0.1, approxPi( 1000 ) );
    SHOULD_BE_APPROX( 3.14159, 0.01, approxPi( 100000 ) );
    SHOULD_BE_APPROX( 3.14159, 0.001, approxPi( 10000000 ) );
    
    std::cout << "\n";
}

A typical run gives reasonable approximations once you get over 100,000 points:
screen-shot-2016-09-16-at-13-30-26

Leave a comment

Filed under C++, C++ Code, Programming

How to learn Python

Having learnt enough Swift to write a neat watch face app for Apple Watch this summer, I thought I’d turn my attention to learning some Python.

Firstly, I wanted a way to edit Python commands in a decent editor and run them. It appears that Xcode doesn’t support this out of the box, but this handy StackOverflow question gives the details on how to set it up.

Secondly, I wanted a series of challenges/tutorials to walk through the basic syntax of Python. HackerRank.com is very good for this sort of thing and has a decent set of exercises to work through.

My first bug was very revealing about the differences between Python and strongly-typed languages like F# and C++:

N = int(raw_input().strip())
if (n > 10):
    print( "big" )
elif ( N < 0 ):
    print( "negative" )
else:
    print( "normal" )

The Python Interpreter doesn’t give an error because of the typo “n > 10” instead of “N > 10” – it just carries on regardless!

Finally, Python Software Foundation seems like a good reference site with lots of examples.

See also the next post in this series.

3 Comments

Filed under Programming, Python