Friday, September 11, 2015

Functions in SWIFT

SWIFT has treated functions as first class members through out. But what do we mean by first-class members? The only way we can find it out is by trying it out.

We will be able to pass function as parameter to functions, use function in return statements. What does it buy you. Well of starters, it allows to inject functions based on various conditions, this way DRY principles are achieved. Better readability and code cohesion.

Here are some examples on the same

/*
* Having functions as first-class citizens
* Return a function. This can be a classic case for creational patterns in the application
*/

func addOne(var num:Int) -> Int {
    return ++num
}

func addTwo(num:Int) -> Int {
    return num + 2
}

func makeIncrementer( incrementerType:String) -> (Int -> Int) {
    switch(incrementerType) {
    case "Double":
        return addTwo
    default:
        return addOne
    }
}

var incrementer = makeIncrementer("single")
print("Value is: \(incrementer(10))")
incrementer = makeIncrementer("Double")

print("Value is: \(incrementer(10))")


/*
* Pass a function as a parameter
* This can be a classic case for validations
*/
func validNumber(num:Int) -> Bool {
    return (num > 100)
}

func hasAnyMatches(numbers:[Int], condition: Int -> Bool) {
    for num in numbers {
        if condition(num) {
            print("Matches the condition, valid number: \(num)")
        }
    }
}

hasAnyMatches([10, 110, 20, 120, 30, 130], condition: validNumber)
// Make anonymous function
hasAnyMatches([8, 13, 12, 10, 5, 3, 14, 16]) { (number:Int) -> Bool in
    return number%2 == 0
}

/*
* Functions with default parameters
* Finally we have an easeir way to handle default parameters
*/
func concatenateString(firstString:String, secondString:String, joiner:String = " ") -> String {
    return firstString + joiner + secondString
}

print(concatenateString("Hello", secondString: "World", joiner:"-"))
print(concatenateString("Hello", secondString: "World"))

/*
* calculate MEAN
*/
func meanForNumbers(sum:Int, count:Int) -> Int {
    return (sum/count)
}
/*
* Variadic parameters
*
*/
func meanForNumbers(mathFunction:(Int,Int)->Int, numbers:Int...) {
    var sum = 0
    for number in numbers {
        sum += number
    }
    let meanForNumbers = mathFunction(sum, numbers.count)
    print("Mean is: \(meanForNumbers)")
}

/*
* Creating function types
*/

func addTwoNumbers(num1:Int, num2:Int) -> Int {
    return (num1 + num2)
}

func multiplyTwoNumbers(num1:Int, num2:Int) -> Int {
    return (num1 * num2)
}

var mathFunction:(Int, Int) -> Int = addTwoNumbers
print("Sum of 2 numbers: \(mathFunction(11, 12))")
mathFunction = multiplyTwoNumbers
print("Product of 2 numbers: \(mathFunction(11, 12))")

/*
* It gets even better, with let. When we use "let", we do not need to pass
* on the type, as always is the case with "let". However the difference
* though is, you get to pass on parameter names. No complaints, anything
* to make the code more readable
*/
let myMathFunction1 = addTwoNumbers
print("Sum of 2 numbers: \(myMathFunction1(11, num2: 12))")
let myMathFunction2 = multiplyTwoNumbers

print("Product of 2 numbers: \(myMathFunction2(11, num2: 12))")

/*
* Nesting functions. With functions being trated as first-class citizens
* we can now nest one function into another, like containment.
*/

func chooseStepFunction(backwards:Bool)->()-> Int {
    var currentStep: Int = 10
    let maxSteps: Int = 100
    let minSteps: Int = 0
    
    func stepForward() -> Int {
        return currentStep + 1
    }
    
    func stepBackward() -> Int {
        return currentStep - 1
    }
    
    return backwards ? stepBackward : stepForward
}

var myNextStepBack = chooseStepFunction(true)
print("My next step is: \(myNextStepBack())")
//Output: My next step is: 9

var myNextStepForward = chooseStepFunction(false)
print("My next step is: \(myNextStepForward())")

//Output: My next step is: 11

No comments: