Treehouse Intermediate Course - Part 10 (Closures 1)
Yes! So completes the aim to get four consecutive days! I actually enjoyed generics more than I thought - the concepts were so difficult but it was interesting finding out about them. Now onto closures - the area of Swift that I find hardest of all! This is apparently a 146 minute course, so I'll likely need at least 5 or 6 entries for this. 24 separate steps, so yes - similar to generics in terms of length! After that, the other iOS intermediate track (there is some crossover there with the Swift one), has a few other projects and bits. So that will be next! OK, let's go!
Start Time - 12:22
Functions as Data
A key point already is that closures are not a type. It is more that they are 'anonymous functions'.
Simple functions - a recap!
Start Time - 12:22
Functions as Data
A key point already is that closures are not a type. It is more that they are 'anonymous functions'.
Simple functions - a recap!
func printString(_ string: String) {
print("Printing the string passed in: \(string)")
}
printString("Jeff")
We can create a constant/variable and assign a function to it. No arguments or parentheses needed!
let stringPrinterFunction = printString
Apparently, in Swift, functions are treated as 'first class'. First class objects! They are entities that support all of the operations available to other types e.g. for Int and String.
Now the stringPrinterFunction constant has the same functionality as the function. Cool!
stringPrinterFunction("Steve")
What about the type for the constant? It's not just a string, Int etc.... Well this one is a (String) -> ().
You can also call that string to void. You can also say it is the signature. OK!
func sum(_ a: Int, _ b: Int) -> Int {
return a + b
}
let addTwoNumbers = sum
So the right hand pane shows addTwoNumbers as ((Int, Int)) -> Int.
Argument labels not needed due to the _.
OK interesting! If I get rid of the underscores, the signature of the addTwoNumbers does not change, nor are the argument labels needed! Right, so the labels are NOT part of the function signature. It boils down to the arguments it accepts and the type it returns. Makes sense.
Extra Reading
Well that was a lovely introduction! Straightforward and totally understandable. There is some suggested reading in the teacher's notes, so I'm having a look...
https://docs.swift.org/swift-book/LanguageGuide/Functions.html
From the function types section. Yes all that seems fine. Ready to move on!
Functions as Parameters
This is tricky - I always found that concept hard! But I'm going to make sure I get this.
Native types (Int, String, Bool etc.) can be assigned into a function as argument labels or outputs.
Right I've done this before Pasan has shown -
func displayString(usingFunction function: (String) -> ()) -> String
I've got the signature from Swift - from the right hand pane.
Right, Pasan has used Void instead of (); that is essentially the same thing and would work but I agree that void makes more sense. And also, no return type needed! As the function we're then going to use with the same signature is the printString one. That does make sense, just about!
displayString(usingFunction: stringPrinterFunction)
I can also use the printString function in the parameter option above. Cool!
Right, code challenge - last bit for now!
func addTwo(to number: Int) -> Int {
return number + 2
}
That was easy - simple stuff so far!
let addition = addTwo
let result = addition(5)
Awesome! Great stuff! So both challenge parts were easy peasy. I forgot to copy and paste the instructions for the above one. But it was no problem; the key pit was to remember not to include the argument name.
OK a good time to pause. More soon.
Paused at 12:52; continued at 13:07 (30 minutes so far).
Right, another half hour I reckon!
Extending the Int Type
So the point of using a function within the function means you can use a general signature in the input, then pass in the relevant function. We may not know the exact Maths operation, but know that it will follow the same signature.
extension Int {
func applyOperation(_ operation: (Int) -> Int) -> Int {
return operation(self)
}
}
So if we make a function that takes in an Int and returns an Int, we can pass it into the applyOperation function! Let's try one out before Pasan does...
This is all before Pasan's explanation -
func square(_ number: Int) -> Int {
return number * number
}
var value = 6
value.applyOperation(square)
Yes! It works well! I could do the same with double, or add another number or some other calculation.
func isMultipleOf5(_ number: Int) -> Bool {
var isMultiple = true
if number % 5 == 0 {
isMultiple = true
} else {
isMultiple = false
}
return isMultiple
}
I could probably make this simpler. But the point is - this function could NOT be passed into the applyOperation function! It does NOT have the same signature!
Right good example from Pasan!
func nearestMultipleOf6(_ number: Int) -> Int {
for x in 1...100 {
let multiple = x * 6
if multiple - value < 6 && multiple > value {
return multiple
} else if multiple == value {
return value
}
}
return 0
}
That all makes sense - having a set signature but the freedom to have a function that can pass in that matches that function. Cool! Next!
Returning Functions
Right, really cool use for type alias. You can create that to use instead of an expression/signature. Then it reads better within the function.
typealias IntegerFunction = (Int) -> Void
func gameCounter() -> (Int) -> Void {
I can now change that signature for the gameCounter function.
func gameCounter() -> IntegerFunction {
func increment(_ i: Int) {
printString("Integer passed in: \(i)")
}
return increment
}
This is now the signature for gameCounter:
() -> (Int) -> ()
However, if we assign gameCounter with parentheses, it changes the signature:
let counter = gameCounter()
(Int) -> ()
That is now the same signature that we had in the return bit of the function.
CHALLENGE TIME!
extension String {
func transform (_: (String) -> String) -> String {
return self
}
}
Let's see if that works... No not quite. The return bit is wrong.
extension String {
func transform (_ string: (String) -> String) -> String {
return string(self)
}
}
YES! That worked - without any help! Needed to put in A name for the parameter.
This is hard! I need some guidance...
extension Character {
var isVowel: Bool {
let vowels: [Character] = ["a", "e", "i", "o", "u", "A", "E", "I", "O", "U"]
return vowels.contains(self)
}
var isConsonant: Bool {
return !self.isVowel
}
}
func removeVowels(from string: String) -> String {
var result: String = ""
for character: Character in string.characters {
if character.isConsonant {
result.append(character)
}
}
return result
}
Yes I had to cheat! Basically I looked up help as everything I tried wasn't working.
OK, good to know that the above doesn't work with Swift now! So no wonder it was so difficult.
*Approx 20 minute delay
let result = "Hello, World!".transform(removeVowels)
Wow, no help needed!
Finish Time - 14:25 (1 hour 28 minute total delay - approx!)
A great session. I was able to access all of it and only got stuck with the challenge 2 that needed conceptualising and code features which did not really match. Anyway, it was all useful experience.
Just to do a summary (definitely worth doing!) to finish:
- Closures are not a 'type'. They have a signature or function expression which has to match.
- You can assign functions to variables/constants and they are 'first class'; they do not need to use the argument labels and maintain the functionality of the native types
- Functions as parameters - need to use the signature that matches the function. Types have to match up basically here.
- Extending types - can do so with use of functions inside. The function again needs its signature for parameters or return type - wherever the function is being passed inside that function - to match.
Next time will be Sunday. So until then!
Comments
Post a Comment