Friday, September 14, 2018

Swift 4 Introduction Series 1.12 - Functions

Function

A function is a set of code that performs a specific task. We group such code into a function so that we can repeat the task again and again by calling the function. We can design a function that takes in parameter. We can also design function that return a data type. When we start learning Swift, the first function that we encounter is the print function.


Defining and Calling Function

To declare or define a function, we must know that there are basically 4 types of functions. They are:
  • Function with no parameter and return value
  • Function with parameter but no return value
  • Function with parameter and return value
  • Function with no parameter but have return value (Rare)


Of course we can have multiple parameter or return value. We can also have nested function (a function within a function).


Defining and Call Function with no Parameters and Return Value

To declare a function, we must have a name. The syntax is as follows:


func <function_name>() {
<set_of_instructions>
}


Example:


func printHello() {
   
   print("Hello!")
   
}




To call the function, we just type the function name with brackets. Syntax is as follows:


<function_name>()


Example:


printHello()




Since print is also a function, this means that we are calling a print function within our function.


Defining and Call Function with Parameters and no Return Value

To declare a function, we must have a name. We also need to include the data type of parameter. The syntax is as follows:


func <function_name>(<parameter_name>:<datatype_parameter>) {
<set_of_instructions>
}


Example:


func printHelloWithName(name: String) {
   
   print("Hello! \(name), how are you?")
   
}




To call the function, we must include the input data with the correct data type in the brackets as define in the function. Syntax is as follows:


<function_name>(<parameter_name>:<input_data>)


Example:


printHelloWithName(name: "Steve")




The print function is also belong to this category.




The first print function takes in any item such as text for variable. The void in front of print tell us that there is no return data.


If we take a look at the third function, this is the function that we have just created. Similarly, void tell us that this print function has no return value. Our function take in a string parameter labeled as name.


Defining and Call Function with Parameters and Return Value

To declare a function, we must have a name, parameter and the datatype of return value. The syntax is as follows:


func <function_name>(<parameter_name>:<datatype_parameter>) -> <dataype_return_value> {
<set_of_instructions>
return <return_value>
}


Example:


func square(input: Int) -> Int {
   
       let answer = input * input
       return answer
   
}




We can simplified the above function to the following:


func square(input: Int) -> Int {
   
       return input * input
   
}


To call the function, we can call the function and at the same time assigned the return value to a constant. The syntax is as follows:


let/var <variable_name> = <function_name>(<parameter_name>:<input_data>)


Example:


let result1 = square(input: 2)
print("result 1 is \(result1)")

let result2 = square(input: 10)
print("result 2 is \(result2)")




This is one of the most common type of function. User provide an input and after processing the input, the result is return as a value.


Defining and Call Function with no Parameters but have Return Value

We can also have a function that produces a return but no input parameters. To declare such a function, we must have a name and the datatype of return value. The syntax is as follows:


func <function_name>( ) -> <dataype_return_value> {
<set_of_instructions>
return <return_value>
}


Such function is very rare. One of possible example is to extract current date. See example below.


Example:


import UIKit

func getCurrentDay() -> Int {
   let cd = Date()
   let calendar = Calendar.current
   let day = calendar.component(.day, from: cd)
   return day
}




To call the function, we can call the function and at the same time assigned the return value to a constant. The syntax is as follows:


let/var <variable_name> = <function_name>()


Example:


let today = getCurrentDay()

print(today)




Complex Function

Calling Function in a Function

In the example above, we already call a function within our function. We can call a function within our function. In fact, that function may call another function. Let's examine the code above again:


import UIKit

func getCurrentDay() -> Int {
   let cd = Date()
   let calendar = Calendar.current
   let day = calendar.component(.day, from: cd)
   return day
}


UIKit is a library of prewritten code and function available for us to use. The function we are using is Date(). This function retrieve current date and time. We also use another function .component(.day, from: cd) to extract the day of the current date.


Nested Function

We can also create a function in a function. This is call a nested function. The following example shows that we are defining 2 function within the main function.


Example:


import UIKit

func getDayOrMonth(choose:Int) -> Int {
   
   switch choose {
   case 1:
       func getCurrentDay() -> Int {
           let cd = Date()
           let calendar = Calendar.current
           let day = calendar.component(.day, from: cd)
           return day
       }
       return getCurrentDay()
   case 2:
       func getCurrentMonth() -> Int {
           let cd = Date()
           let calendar = Calendar.current
           let month = calendar.component(.month, from: cd)
           return month
       }
       return getCurrentMonth()
   default:
       print("Please choose 1 for day and 2 for month")
       return 0
   }
   
}




Call the nested function:


let dday = getDayOrMonth(choose: 1)
let dmonth = getDayOrMonth(choose: 2)
let other = getDayOrMonth(choose: 5)




For the last call, it will also print a statement as shown below:


Function with Multiples Parameters

We can also create function with multiples parameters.


Example:


/*
// The following function attempt to calculate exponential where x is the base and y is the exponent
// Please note that this function is accurate for small number
// For very big and small number please refer to the official library provided in various programming language
// In swift we can use pow(Double, Double)
*/
func powerOf(x:Double, y: Int) -> Double {
  
   
   var answer = x
   
   if y != 1 {

       for _ in 2...y {
           answer *= x
       }
   }
   return answer
   
}


The function above takes in a double, an integer and return the answer as double. Implementation is as follows:


powerOf(x: 3, y: 2)
powerOf(x: 5, y: 5)
powerOf(x: 1.03, y: 20)




Next we will write a function with rate of return.


/*
This function will calculate total compounding return. it will take input rate in percent, compounding period in years, and principal amount.
The formula is ((1 + (rate/100))^period ) * principal amount
*/
func compoundReturn(rateInPercent: Int, periodInYears: Int, principalAmount:Double) -> Double {
  
   var rate = 0.0
   if rateInPercent <= 100 && rateInPercent > 0 {
       rate = Double(rateInPercent)
   }
   
   return pow((1 + (rate/100)), Double(periodInYears)) * principalAmount
   
}


The implementation is as follows:


let inflation = 3
let yearsLater = 20
let dollarValue = 5000.00
let itWorthThisMuch = compoundReturn(rateInPercent: inflation, periodInYears: yearsLater, principalAmount: dollarValue)
print("With inflation of \(inflation)%. The value of $\(dollarValue) is equivalent to \(itWorthThisMuch) in todays value.  ")




Function with Array and Tuples

We can use function with array or tuples as input. We will use example that use a tuple as input.


/*
This function will calculate total compensation. It will take worker profile which include hours worked and the hourly rate as input and return the compensation amount.
*/
func totalCompensation(worker: (name: String, workHours: Int, rate:Double)) -> Double {
   
   return Double(worker.workHours) * worker.rate
}


The implementation is as follows:


let worker1 = (name: "Tim", workHours: 100, rate: 20.0)
print("The total compensation for \(worker1.name) for \(worker1.workHours) hours at the rate of $\(worker1.rate) per hour is $\(totalCompensation(worker: (name:worker1.name, workHours:worker1.workHours, rate:worker1.rate)))")


The result is as follows:


We can also take in tuple and return tuple as shown in similar example below.


/*
This function will calculate total compensation plus 401K deduction and employer contribution. It will take work profile (tuple) as input and return the compensation and contribution matrix (tuple).
*/
func totalCompensationMatrix(worker: (name: String, workHours: Int, rate:Double)) -> (compensation: Double, worker401Deduction: Double, takeHomePay:Double, employer401Contribution: Double) {
   
   let compensation = Double(worker.workHours) * worker.rate
   let worker401Deduction = compensation * 0.1
   let takeHomePay = compensation - worker401Deduction
   let employer401Contribution = compensation * 0.03
   
   return (compensation, worker401Deduction, takeHomePay, employer401Contribution)
}


Implementation as follows:


let worker2 = (name: "Steve", workHours: 95, rate: 30.0)
let computeResult = totalCompensationMatrix(worker: worker2)

print("The total compensation for \(worker2.name); working for \(worker2.workHours) hours at the rate of $\(worker2.rate) per hour is $\(computeResult.compensation)")
print("\(worker2.name)'s 401 deduction will be $\(computeResult.worker401Deduction). Therefore, the take home pay will be $\(computeResult.takeHomePay).")
print("Please also note that \(worker2.name)'s employer also contributed $\(computeResult.employer401Contribution) to \(worker2.name)'s 401k.")




For the next example, we will be showing a function taking an array as input and return a tuple. This example will be a simple computation on basic statistics.


Example:


/*
This function will take in a basic integer array and compute the mean, median, min and max. The result will return as a tuple
*/
func basicStat(inputArray: [Int]) -> (mean: Double, median: Int, min:Int?, max: Int?) {
   
   var total = 0
   for number in inputArray {
       total += number
   }
   
   let mean = Double(total/inputArray.count)
   let min = inputArray.min()
   let max = inputArray.max()
   
   let reorderList = inputArray.sorted()
   let indexPosition = (inputArray.count/2)
   let median = reorderList[indexPosition]
   
   return (mean, median, min, max)
}


The implementation is as follows:


let testArray = [12,45,23,6,73,252,98,63,72,23,12,14,62,36,98]

let theStatResult = basicStat(inputArray: testArray)

print(testArray)
print("For the array above, the average is \(theStatResult.mean).  ")
print("The minimum and maximum value in the array are \(theStatResult.min!) and \(theStatResult.max!).")
print("Finally, the median for this array is \(theStatResult.median). ")




What happen if we input an empty array to the function above? It will generate error because we did not check for empty array. So it is best we make entire tuple as optional.


/*
This function will take in a basic integer array and compute the mean, median, min and max. The result will return as a tuple
*/
func basicStatv2(inputArray: [Int]) -> (mean: Double, median: Int, min:Int, max: Int)? {
   
   if inputArray.isEmpty {
       return nil
   }
   
   var total = 0
   for number in inputArray {
       total += number
   }
   
   let mean = Double(total/inputArray.count)
   let min = inputArray.min()
   let max = inputArray.max()
   
   let reorderList = inputArray.sorted()
   let indexPosition = (inputArray.count/2)
   let median = reorderList[indexPosition]
   
   return (mean, median, min!, max!)
}
The implementation is as follows:


let testArray2 = [12,45,23,6,73,252,98,63,72,23,12,14,62,36,98]

if let theStatResult2 = basicStatv2(inputArray: testArray2) {
   print(testArray2)
   print("For the array above, the average is \(theStatResult2.mean).  ")
   print("The minimum and maximum value in the array are \(theStatResult2.min) and \(theStatResult2.max).")
   print("Finally, the median for this array is \(theStatResult2.median). ")
} else {
   print("This is an empty array.")
}




We have the same result. Now lets input an empty array.


let testArray3 = [Int]()

if let theStatResult3 = basicStatv2(inputArray: testArray3) {
   print(testArray3)
   print("For the array above, the average is \(theStatResult3.mean).  ")
   print("The minimum and maximum value in the array are \(theStatResult3.min) and \(theStatResult3.max).")
   print("Finally, the median for this array is \(theStatResult3.median). ")
} else {
   print("This is an empty array.")
}




Please also note that a return type of (Int?, Int?) is different from return type (Int, Int)?.


Parameter and Argument Name in Function

Parameters are the input variables in a function. So parameters name are the label that describe the input variables. In Swift, parameter name is a must.


Argument are the data we supply when calling a function. In Swift, if we have parameters named. Then, we must also use the same name when calling the function.




In summary, parameter name are the name we supply when defining function input data. Argument name are the name we used when calling function. By default, the parameter name and argument name is the same.


However, Swift allow flexibility when using argument and parameter name. Therefore argument name and parameter name can be different.


Therefore the syntax that separate argument name and function name should be


func <function_name>(<argument_name> <parameter_name>:<datatype_parameter>...) -> <dataype_return_value> {
<set_of_instructions>
return <return_value>
}


When calling function


let/var <variable_name> = <function_name>(<argument_name>:<input_data>)


The following example show how to define function with different argument and parameter name:


func showDistance(from startIdx:Int, to endIdx:Int) {
   print("The distance between \(startIdx) to \(endIdx) is \(endIdx-startIdx).")
}

showDistance(from: 234, to: 276)




We can also omit argument name.


func showDistance2(_ startIdx:Int, _ endIdx:Int) {
   print("The distance between \(startIdx) to \(endIdx) is \(endIdx-startIdx).")
}

showDistance2(12, 34)


In the next example, we use the same function but we omit the first argument and for the second argument, we use the parameter name.


func showDistance3from(_ from:Int, to:Int) {
   print("The distance between \(from) to \(to) is \(to-from).")
}

showDistance3from(6546, to: 6587)




What happen if we omit both argument and parameters. We will have a useless function that could not make use of the input.


func useLess(_: Int, _:Int) {
   
   print("This function cannot make use of your input.")
   
}

useLess(12, 43)




The general rule is as follows:
  • The first label is argument name and second label is parameter name.
  • If only parameter name is supplied, then argument name is the same as parameter name.
  • If parameter name is supplied, we must use the same name as argument.
  • Argument name can be omitted if there is underscore in function definition.


Working with Function Parameters

The following section discuss various way we can work with function parameters.


Parameters with Default

We can supply default value to our parameters so that there will always have a value. To define default value we just include the default value after parameter definition.


func <function_name>(<parameter_name>:<datatype_parameter_with_default>)  -> <dataype_return_value>  {
<set_of_instructions>
return <return_value>
}


Example:


func deviceSelection(color: String = "Silver", RAM: Int = 64) {
   
   print("This user selected \(color) with \(RAM)GB of RAM for his phone.")
}

deviceSelection(color: "SpaceGrey", RAM: 128)
deviceSelection()
deviceSelection(color: "Gold")
deviceSelection(RAM: 256)




In the example above, we can supply all the input, no input or we can supply only one input. For function with mixed of default and non-default value, it is advisable to place parameters with no default first. However, no error will occurs if we put parameters with no default value last.


Variadic Parameters

In the previous example where we accept an array as input. We can configure our parameters so that it will accept an unknown number of variable of same data type. This is known as variadic parameters. To use variadic parameter, we include 3 dots (...) just after the data type:


func <function_name>(<parameter_name>:<datatype_parameter>...)  -> <dataype_return_value>  {
<set_of_instructions>
return <return_value>
}


Example:


func averageOf(_ numbers: Double...) {
   
   if numbers.isEmpty {
       print("There is no data!")
   } else {
       var total:Double = 0
       for number in numbers {
           total += number
       }
       let average = total/Double(numbers.count)
       print(numbers)
       print("The average of the numbers listed above is \(average).")
   }
}

averageOf(12,23,32,12,45,53)
averageOf()




Note: Please note that a function can have at most 1 variadic parameters.


In-Out Parameters

Please note that all parameters are created as constant, therefore, if there is any issue with the function it will not affect other parts of the program. However, there will be instances where we need to manipulate and affect some global variable.


To pass a global variable that we can manipulate and pass the same variable out, we need to add the keyword inout at the parameters and while calling the function, we need to prefix & at the argument.


func <function_name>(<parameter_name>:inout <datatype_parameter>)  -> <dataype_return_value>  {
<set_of_instructions>
return <return_value>
}


To call the function:


let/var <variable_name> = <function_name>(&<argument_name>:<input_data>)


Please also note the following:
  • In-Out parameters cannot have default value
  • Variadic parameters cannot be marked as inout


Example:


/*
This function add up spending per day. It also add up cumulative total and count how many time this function is called.
*/

func calculateTodaySpending(gTotalSpend: inout Double, gFunction:inout Int, spending numbers: Double...) {
   
   if numbers.isEmpty {
       print("There is no data!")
   } else {
       var total:Double = 0
       for number in numbers {
           total += number
       }
       
       gTotalSpend += total
       gFunction += 1
       print("The total spending for this day is $\(total).")
       print("Grand total spending is $\(gTotalSpend).")
       print("This function is trigger \(gFunction) times.")
   }
}


The implementation is as follows:


var gTotalSpend:Double = 0
var gFunction = 0

calculateTodaySpending(gTotalSpend: &gTotalSpend, gFunction: &gFunction, spending: 21, 12, 87.0, 54.76, 196.4)

calculateTodaySpending(gTotalSpend: &gTotalSpend, gFunction: &gFunction, spending:13,87.87)

calculateTodaySpending(gTotalSpend: &gTotalSpend, gFunction: &gFunction, spending:65.0, 12.50)




In-out parameters are another way for a function affect outside of the scope of its function body.


Advance Function Concept

As we have learned earlier Int, String or Bool are data type. Function can also be a data type. Function can be passed around as input parameter and return data type.


Function Types

Function can be considered as data type. It is known as Function Type. A function type is defined by the data type of the input parameter and return data. For example, a function that take in 2 Int and return data as Int. The function type can be written as: (Int, Int) -> Int. Consider the following function:


func addTwoNumber(_ a: Int, _ b: Int) -> Int {
   return a + b
}

func multiplyTwoNumber(_ a: Int, _ b: Int) -> Int {
   return a * b
}


The 2 functions above have different operation however, they have the same function type of (Int, Int) -> Int. Function with different operation can have same function type. A function type that does not take any parameter and does not return any data can be written as () -> Void. Similarly, a function type that take in an integer but does not return any data is written as (Int) -> Void.  We can use function type as variable or constant.


Example:


func addTwoNumber(_ a: Int, _ b: Int) -> Int {
   return a + b
}

func multiplyTwoNumber(_ a: Int, _ b: Int) -> Int {
   return a * b
}

var calculate2Number: (Int, Int) -> Int

calculate2Number = addTwoNumber(_:_:)
calculate2Number(2,3)
calculate2Number = multiplyTwoNumber(_:_:)
calculate2Number(2,3)
let newMathFunction = addTwoNumber
newMathFunction(2,3)




In the example above, we have created 2 function with the same function type. Then we create a variable labeled as calculate2Number. Then we assign any of the function to this variable. Then we called the function using the variable. This demonstrate that we can use function type as  like any other variable or constant.


Let us consider another example. If we want to calculate Fahrenheit from Celsius; the formula is degrees in Celsius x (9/5) + 32. To compute back to Celsius, the formula is degrees in ((Fahrenheit -32) x (5/9)). To create 2 function the standard way, we have the following functions:


func calF (_ degree: Double) -> Double {
   return (degree * (9/5)) + 32
}

func calC (_ degree: Double) -> Double {
   return (degree - 32) * (5/9)
}


calF(0)
calC(105)

let convertfromCtoF = calF
convertfromCtoF(38)

let convertfromFtoC = calC
convertfromFtoC(0)




Listed below, we use the same example but different ways of implementation


func calF (_ degree: Double) -> Double {
   return (degree * (9/5)) + 32
}

func calC (_ degree: Double) -> Double {
   return (degree - 32) * (5/9)
}


func convertTemperature(_ degree: Double, convertTo: String) {
   
   switch convertTo {
   case "F", "f", "Fahrenheit", "fahrenheit":
       let convertToFahrenheit = calF
       print("\(degree) degree Celsius is \(convertToFahrenheit(degree)) Fahrenheit ")
   case "C", "c", "Celsius", "celsius":
       let convertToCelsius = calC
       print("\(degree) degree Fahrenheit is \(convertToCelsius(degree)) Celsius ")
   default:
       print("Invalid description, please center c/C for Celsius or f/F for Fahrenheit")
   }
}

convertTemperature(34, convertTo: "F")
convertTemperature(60, convertTo: "c")




Function as Input Parameter

We can use function type as input parameter for another function. We use the function type such as (Double) -> Double or (Int, Int) -> Int as the data type in the parameter field.


Please see the example below:


func calF (_ degree: Double) -> Double {
   return (degree * (9/5)) + 32
}

func calC (_ degree: Double) -> Double {
   return (degree - 32) * (5/9)
}

let calculateFahrenheit = calF
let calculateCelsius = calC

func calTemp(_ degree: Double, _ tempFunc: (Double)-> Double) {
   
   
   print("The converted temperature is \(tempFunc(degree))")
   
}

calTemp(0, calculateFahrenheit)
calTemp(35, calculateFahrenheit)
calTemp(90, calculateCelsius)




In the example above, we created a third function calTemp to calculate any temperature conversion. This function take in a function with function type (Double)->Double. When this function is called, we can pass any function with the same function type to this function. The role of the third function is to just print the computed result.


Function as Return Type

Similarly, we can also use function type as return type. We just need to write the function type immediately after (->) of a function.


Using the temperature conversion function as the base example, we create a third function that return any function stipulate by the user.


func calF (_ degree: Double) -> Double {
   return (degree * (9/5)) + 32
}

func calC (_ degree: Double) -> Double {
   return (degree - 32) * (5/9)
}

let calculateFahrenheit = calF
let calculateCelsius = calC

func chooseTempConversion (_ choice: String) -> (Double) -> Double {
   
   func doNOthing (_ : Double) -> Double {
       return 0.0
   }
   
   switch choice {
   case "F", "f", "Fahrenheit", "fahrenheit":
       return calculateFahrenheit
   case "C", "c", "Celsius", "celsius":
       return calculateCelsius
   default:
       print("Invalid description, please center c/C for Celsius or f/F for Fahrenheit")
       return doNOthing(_:)
   }
   
}

let myFunction = chooseTempConversion("f")
myFunction(33)
let myFunction2 = chooseTempConversion("c")
myFunction2(10)
let myFuction3 = chooseTempConversion("x")
myFuction3(100)




Nested Function with Function Type as Return

If you notice in the previous example, we have a doNothing function that is enclosed in the third function. In Swift, by default nested function can only be used within the function. However, if we use function as return type, we can use these function in the main module.


First, we would like to prove that nested function cannot be used outside.



func printTempConversion (_ choice: String, degree: Double) {
   
   func calculateFahrenheit (_ degree: Double) -> Double {
       return (degree * (9/5)) + 32
   }
   
   func calculateCelsius (_ degree: Double) -> Double {
       return (degree - 32) * (5/9)
   }
   
   switch choice {
   case "F", "f", "Fahrenheit", "fahrenheit":
       print("Conversion to Fahrenheit is \(calculateFahrenheit(degree))")
   case "C", "c", "Celsius", "celsius":
       print("Conversion to Celsius is \(calculateCelsius(degree))")
   default:
       print("Invalid description, please center c/C for Celsius or f/F for Fahrenheit")
       
   }
   
}


Proper implementation is as follows:


printTempConversion("f", degree: 100)
printTempConversion("c", degree: 5)
printTempConversion("x", degree: 0)




What happen if we just call the nested function outside the printTempConversion function?


The system could not identify the nested function.


Now if we return these function as function type.


func chooseTempConversion (_ choice: String) -> (Double) -> Double {
   
   func calculateFahrenheit (_ degree: Double) -> Double {
       return (degree * (9/5)) + 32
   }
   
   func calculateCelsius (_ degree: Double) -> Double {
       return (degree - 32) * (5/9)
   }
   
   func doNothing (_: Double) -> Double {
       return 0.0
   }
   
   switch choice {
   case "F", "f", "Fahrenheit", "fahrenheit":
       return calculateFahrenheit(_:)
   case "C", "c", "Celsius", "celsius":
       return calculateCelsius(_:)
   default:
       print("Invalid description, please center c/C for Celsius or f/F for Fahrenheit")
       return doNothing(_:)
       
   }
   
}


The nested function still could not be called directly


However, using proper implementation by capturing from the return function. We can call these function indirectly.


***

No comments:

Post a Comment