Skip to content

Instantly share code, notes, and snippets.

@davidkneely
Created May 5, 2017 10:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davidkneely/1d78b5f43d87fa5551a467c308567d15 to your computer and use it in GitHub Desktop.
Save davidkneely/1d78b5f43d87fa5551a467c308567d15 to your computer and use it in GitHub Desktop.
closures001 talk 2017_5_4 at Hawaii iOS Developer Meetup
//: Playground - noun: a place where people can play
// Hawaii iOS Developer Meetup
// Closures
// 2017_5_4
// 6pm
import UIKit
// Let's define a closure. Questions? Thoughts?
/*
Closures - "Discrete bundles of functionality." - BNR
"Nameless functions" - This function has no name.
"First-class functions" -
1) Can be passed into other functions as parameters
2) Can be returned by functions
Captures variables defined within its scope.
*/
// Tonight's agenda:
/// Part I - Transform a free function into a nameless closure.
/// Part II - Compress the closure into a single line for functionality.
/// Part III - Functions as return types
/// Part IV - Functions as arguments
/// Part V - Closures capture Values
/// Part VI - Closures as Reference Types
/// Part VII - Functional Programming
/// Part VIII - Swift's Higher Order Functions: Map
/// Part IX - Swift's Higher Order Functions: Filter
/// Part X - Swift's Higher Order Functions: Reduce
/// Part XI - Mob Programming Challenge!!!
/// Part I - Transform a free function into a nameless closure.
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// BNR Swift Programming book
// Chapter 13, page 127
let volunteerCounts = [1, 3, 40, 32, 2, 53, 77, 13]
func sortAscending(_ i: Int, _ j: Int) -> Bool {
return i > j
}
let volunteersSorted001 = volunteerCounts.sorted(by: sortAscending)
// free function sortAscending is applied to volunteerCounts array
// Next we introduce the trailing closure syntax
// Let's define "Closure Expression Syntax." It's also know as "Trailing Closure Syntax"
/*
{(parameters) -> return_type in
// code
}
*/
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// BNR Swift Programming book
// Chapter 13, page 129
// Let's convert the normal function into trailing closure syntax in 5 steps
func sortAscending002(_ i: Int, _ j: Int) -> Bool {
return i > j
}
// Step 1: Remove the name of the function and the keyword 'func'
// NOTE: This code will not compile until we've done all the steps
//(_ i: Int, _ j: Int) -> Bool {
//
// return i > j
//}
// Step 2: Move the function signature into the curly braces
// NOTE: This code will not compile until we've done all the steps
//{(_ i: Int, _ j: Int) -> Bool
//
// return i > j
//}
// Step 3: Add in the trailing closure syntax with keyword 'in' after the arrow return type
// NOTE: This code will not compile until we've done all the steps
//{(_ i: Int, _ j: Int) -> Bool in
//
// return i > j
//}
// Step 4: Apply the closure to an Array
// NOTE: This code will not compile until we've done all the steps
//volunteerCounts.sorted(by: {(_ i: Int, _ j: Int) -> Bool in
//
// return i > j
//})
// Step 5: Assign the closure to a constant or variable to accept the return
var volunteersSorted002 = volunteerCounts.sorted(by: {
(_ i: Int, _ j: Int) -> Bool in
return i > j
})
/// Part II - Compress the closure into a single line for functionality.
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// BNR Swift Programming book
// Chapter 13, page 129
// Next we're going to compress the closure so it looks cleaner and takes up less lines of code in 4 steps
// Step 1: Remove all type annotations from the parameters and the return
// NOTE: This code will not compile until we've done all the steps
//let volunteersSorted003 = volunteerCounts.sorted(by: {
//
// (_ i: , _ j: ) -> in
//
// return i < j
//
//})
// Step 2: Move entire expression onto one line
// NOTE: This code will not compile until we've done all the steps
//let volunteersSorted003 = volunteerCounts.sorted(by: {(_ i: , _ j: ) -> in return i < j })
// Step 3: Remove the underscores and arrow.
// NOTE: This code will not compile until we've done all the steps
//let volunteersSorted003 = volunteerCounts.sorted(by: {(i, j) in return i < j })
// Step 4: Remove the keyword 'return'
let volunteersSorted003 = volunteerCounts.sorted(by: {(i, j) in i < j })
/// Let's make it even more compact by using swift specific closure shorthand syntax
// Update code to use shorthand for arguments (no need to name them anymore)
//let volunteersSorted004 = volunteerCounts.sorted(by: {(i, j) in i < j })
// Step 1: Remove the parameters
// NOTE: This code will not compile until we've done all the steps
//let volunteersSorted004 = volunteerCounts.sorted(by: { in i < j })
// Step 2: Remove the the keyword 'in'
// NOTE: This code will not compile until we've done all the steps
//let volunteersSorted004 = volunteerCounts.sorted(by: { i < j })
// Step 3: Rename i and j with (swift specific) closure shorthand
let volunteersSorted004 = volunteerCounts.sorted(by: { $0 < $1 }) // with arrays only use $0
/// And finally, if the closure is passed to a function's final argument, it can be written in line
//let volunteersSorted005 = volunteerCounts.sorted(by: { $0 < $1 })
// Step 1: Remove the argument name 'by'
// NOTE: This code will not compile until we've done all the steps
//let volunteersSorted005 = volunteerCounts.sorted({ $0 < $1 })
// Step 2: Remove the parens that surround the closure
// NOTE: This code will not compile until we've done all the steps
let volunteersSorted005 = volunteerCounts.sorted { $0 < $1 } // can be many arguments in the function call, but must be the last in the list.
// question, how can we remove the parens? If it's the last parameter we can remove named parameter.
/// Part III - Functions as return types
func makeTownGrand() -> (Int, Int) -> Int { // anything after the first arrow is a function
func buildRoads(byAddingLights lights: Int, toExistingLights existingLights: Int) -> Int {
return lights + existingLights
}
return buildRoads
}
// function makeTownGrand takes no arguments
// makeTownGrand returns a function that takes two arguments
// that returned function returns an integer.
// buildRoads is implemented in the function makeTownGrand
// buildRoads is a nested function
// no need to express the implementation details to outer function
// buildRoads must conform to function definition expressed in makeTownGrand function definition.
// this means that buildRoads must take two Ints and return an Int
// Now let's test out this function makeTownGrand
var stoplights = 4
let townPlanByAddingLightsToExistingLights = makeTownGrand() // assigns function as return type
townPlanByAddingLightsToExistingLights(4, stoplights) // runs the returned function with arguments
print("Knowhere has \(stoplights) stoplights"
/// Part IV - Functions as arguments
// Adds 'budget' and 'condition' parameters to outer function makeTownGrand
// 'condition' argument will take a function as we can see in the method signature
// This is why we can pass in the function evaluate with no parameter because it returns a Bool
// Sets up conditional check on 'budget'
// NOTE: The return type is now an optional because the function might return nil
func makeTownGrand(withBudget budget: Int, condition: (Int) -> Bool) -> ((Int, Int) -> Int)? { // one closure to another closure
if condition(budget) { // Adds in conditional
func buildRoads(byAddingLights lights: Int, toExistingLights existingLights: Int) -> Int {
return lights + existingLights
}
return buildRoads
} else { // more conditional code
return nil
}
}
func evaluate(budget: Int) -> Bool {
return budget > 10_000
}
var stoplights001 = 4
if let townPlanByAddingLightsToExistingLights002 = makeTownGrand(withBudget: 1_000, condition: evaluate) { // Do you know why budget does not need a passed in parameter? Because it's a function!
stoplights001 = townPlanByAddingLightsToExistingLights002(4, stoplights001)
}
// Update the budget to get more roads by uncommenting the next function:
//if let townPlanByAddingLightsToExistingLights003 = makeTownGrand(withBudget: 100_000, condition: evaluate) { // I am unclear on why budget does not need a passed in parameter
//
// stoplights001 = townPlanByAddingLightsToExistingLights003(4, stoplights001)
//}
/// Part V - Closures capture Values
// populationTracker(growth: Int) is a nested function
// populationTracker(growth: Int) captures the initialPopulation from the enclosing function
// makePopulationTracker(forInitialPoplulation:) takes a single argument
// population is a running count of people in town
func makePoplulationTracker(forInitialPoplulation population: Int) -> (Int) -> Int {
var totalPopulation = population
func populationTracker(growth: Int) -> Int {
totalPopulation += growth
return totalPopulation
}
return populationTracker
}
var currentPopulation = 5_422
let growBy = makePoplulationTracker(forInitialPoplulation: currentPopulation)
growBy(500) // function is keeping internal count going
growBy(500)
growBy(500)
currentPopulation = growBy(500) // assign to var when you are finished
/// Part VI - Closures as Reference Types
// When you assign a closure to a constant or variable, you are setting that constant or variable to POINT TO a function.
// You are not creating a distinct copy of that function.
// BEWARE: Any information captured by the function's scope will be changed if you call the function via a new constant or variable.
let anotherGrowBy = growBy
anotherGrowBy(500) // still referncing the captured population variable in growBy
var bigCityPopulation = 4_061_981
let bigCityGrowBy = makePoplulationTracker(forInitialPoplulation: bigCityPopulation)
bigCityPopulation = bigCityGrowBy(10_000)
currentPopulation // shows that the two populations are distinct from each other
/// Part VII - Functional Programming
/*
Tenents of Functional Programming: (Jessica Kerr)
Data In, Data Out
*/
// Discussion time
// Questions? Comments?
/// Part VIII - Swift's Higher Order Functions: Map
// Use map to transform an array's contents (not in place!)
// example: Square all elements in the array.
let precintPopulation = [1244, 2021, 2157]
let projectedPopulations = precintPopulation.map {
(population: Int) -> Int in
return population * 2
}
/// Part IX - Swift's Higher Order Functions: Filter
// Use filter to select certain elements from the input array and discard the rest (not in place!)
// example: Take out all the odd numbers in the array.
let bigProjections = projectedPopulations.filter {
(projection: Int) -> Bool in
return projection > 4000
}
/// Part X - Swift's Higher Order Functions: Reduce
// Use reduce to take all of the values in the input array and produce a single value (not in place!)
// example: Add up all the elements in the array and print out their sum.
let totalProjection = projectedPopulations.reduce(0) {
(accumulatedProjection: Int, precinctProjection: Int) -> Int in
return accumulatedProjection + precinctProjection
}
//BONUS: Transform each of the preceding higher-order functions to closure shorthand. Can you get them to fit on one line?
// MOB PROGRAMMING CHALLENGE:
// Take the following imperative code and update it to use higher order functions to achieve the same result.
// Bonus points for having zero state within the method.
// Bonus points for doing ZERO assignment statements :)
/// Squares all elements in Array, then removes the odd numbers, and then adds up all remaining numbers.
/// :param: None
/// :return: Int
func squareThenTakeOutOddsThenReturnSum() -> Int {
let inputArray = [1, 3, 40, 32, 2, 53, 77, 13]
var tempArray = [Int]()
var tempArray2 = [Int]()
var returnInt = 0
// Square the numbers
for index in 0...inputArray.count - 1 {
let returnElement = inputArray[index] * inputArray[index]
tempArray.append(returnElement)
}
// Remove the odds
for index in 0...tempArray.count - 1 {
if(tempArray[index] % 2 == 0) {
tempArray2.append(tempArray[index])
}
}
// Add up the elements in the list
for index in 0...tempArray2.count - 1 {
returnInt = returnInt + tempArray2[index]
}
// return the sum
return returnInt
}
squareThenTakeOutOddsThenReturnSum()
// SOLUTION WE CAME UP WITH IN 15 MINUTES:
import UIKit
/// Squares all elements in Array, then removes the odd numbers, and then adds up all remaining numbers.
/// :param: None
/// :return: Int
func squareThenTakeOutOddsThenReturnSum() -> Int {
let inputArray = [1, 3, 40, 32, 2, 53, 77, 13]
let temp1 = inputArray.map({$0 * $0}).filter({$0 % 2 == 0}).reduce(0){($0+$1)}
return temp1
}
squareThenTakeOutOddsThenReturnSum()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment