June 9, 2014

Swift, Functional Programing, Structs and Fold

Posted in Uncategorized tagged , , , , at 10:08 pm by tetontech

In the last post I gave a pretty lame example of how to use fold. It was assembling a string from an array of numbers. Since the emphasis of that post was the source code for foldl and foldr I thought a lame but simple example would be OK. In this post I’m giving a computationally more significant example.

Let’s say you have an bunch of gerbils you are tracking and you need to know how many males and females there are. To simulate data input and storage routines outside the scope of this example let’s generate a series of pseudo-random gerbils and put them in an Array for later use.

    /*
    * example setup code
    */
    var gerbils:Array<Gerbil> = []
        
    for index in 0...20{
        /*
        * arc4random returns a UInt32. Create an Int from it.
        */
        let anAge = Int(arc4random() % 5)
        let aSex = Int(arc4random() % 2)
        /*
        * use Swift's ternary operator to change the aSex
        */ integer into the representative string
        gerbils.append(Gerbil(age: anAge, sex: aSex == 1 ? "female" : "male"))
    }

    /*
     * Gerbil struct source
     */     
    struct Gerbil{
         var age:Int
         var sex:String
    }

Now let’s take this Array of Gerbil structs and find out how many males and females there are. Either foldl or foldr would work so lets pick foldl. Swift’s standard reduce method would not work since reduce would require a Gerbil to be returned when given an Array of Gerbils. That doesn’t make sense here nor does that restriction make sense in most situations. That’s why we need fold methods for the Array class (See the previous post for the fold source code).

In this example foldl is passed an initial countBySex struct called statsWrapup that has both the male and female counts set to zero. Selecting zero for initialization makes it easy to increment the values, using Int’s ++ operator, when we find a male or female Gerbil. This detection and incrementation is done using Swift’s Ternary operator ?:

        /*
         * example begins
         */
        var statsWrapup = gerbils.foldl(countBySex(males:0, females:0)){
            var (ageBySexTracker, aGerbil) = $0
            aGerbil.sex == "female" ? ageBySexTracker.females++ : 
                              ageBySexTracker.males++
            return ageBySexTracker
        }
        println("females: \(statsWrapup.females) males: \(statsWrapup.males)")

        /*
         * countBySexStruct source
         */
         struct countBySex{
            var males:Int
            var females:Int
        }

There are many other choices that could have been made for this example. The sex indicator could have been left an Int with 0 or 1 potential values, the sex indicator could have been a boolean value with true for female and false for male, an Enumeration could have been created and used as the sex indicator, etc. but I chose not to do this for example clarity and to focus on why fold needed to be used instead of reduce.

Could this have all been done using a standard for loop? Of course. But why should we make that choice. It will increase the ‘glue’ code in our application that is written repeatedly and used rarely. It is well known that each line of code we write is a new potential bug. One way to reduce the bugginess of code is to stop creating code that is rarely used. By wrapping the looping code into a pre-exiting function, such as map, reduce, or fold, we execute the code many times. This can aid and speed debugging.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: