Skip to content

Latest commit

Β 

History

History
539 lines (380 loc) Β· 11.1 KB

HigherOderFunction.md

File metadata and controls

539 lines (380 loc) Β· 11.1 KB

Higher-Oder-Function(κ³ μ°¨ν•¨μˆ˜)

  • ν•˜λ‚˜ μ΄μƒμ˜ ν•¨μˆ˜λ₯Ό 인자둜 μ·¨ν•˜λŠ” ν•¨μˆ˜
  • ν•¨μˆ˜λ₯Ό 결과둜 λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜

(β€» Higher-order Function이 되기 μœ„ν•΄μ„œλŠ” ν•¨μˆ˜κ°€ First-class Citizen 이어야 ν•œλ‹€.)

1κΈ‰ 객체 (First-class citizen)

  • λ³€μˆ˜λ‚˜ 데이터에 ν• λ‹Ήν•  수 μžˆμ–΄μ•Ό ν•œλ‹€.
  • 객체의 인자둜 λ„˜κΈΈ 수 μžˆμ–΄μ•Ό ν•œλ‹€.
  • 객체의 λ¦¬ν„΄κ°’μœΌλ‘œ 리턴할 수 μžˆμ–΄μ•Ό ν•œλ‹€.
func firstClassCitizen() {
  print("function call")
}

func function(_ parameter: @escaping ()->()) -> (()->()) {
  return parameter
}

let returnValue = function(firstClassCitizen)
returnValue
returnValue()

forEach

  • μ»¬λ ‰μ…˜μ˜ 각 μš”μ†Œ(Element)에 동일 연산을 μ μš©ν•˜λ©°, λ°˜ν™˜κ°’μ΄ μ—†λŠ” ν˜•νƒœ
let immutableArray = [1, 2, 3, 4]

for num in immutableArray {
  print(num, terminator: " ")
}
print()


immutableArray.forEach { num in
  print(num, terminator: " ")
}
print()


immutableArray.forEach {
  print($0, terminator: " ")
}
print()


func printParam(_ num: Int) {
  print(num, terminator: " ")
}
immutableArray.forEach(printParam(_:))
print()

Question

  • forEach 와 for 문의 차이점은?
  • 1~10 κΉŒμ§€μ˜ 숫자 쀑 짝수만 좜λ ₯ν•˜λ‹€κ°€ 9κ°€ 되면 μ’…λ£Œλ˜λ„λ‘ forEachλ₯Ό μ΄μš©ν•΄ κ΅¬ν˜„
print("\n---------- [ A. for vs forEach ] ----------")

/*
 for문은 λ°˜λ³΅λ¬Έμ΄λ―€λ‘œ break, continue ν‚€μ›Œλ“œ μ‚¬μš©
 forEach문은 ν•¨μˆ˜(클둜져)μ΄λ―€λ‘œ break, continue λŒ€μ‹  return ν‚€μ›Œλ“œ μ‚¬μš©
 */

for i in 1...10 {
  guard i != 9 else { break }
  guard i % 2 == 0 else { continue }
  print(i, terminator: " ")
}
print()

(1...10).forEach {
  guard $0 < 9 else { return }
  guard $0 % 2 == 0 else { return }
  print($0, terminator: " ")
}
print()

map

  • μ»¬λ ‰μ…˜μ˜ 각 μš”μ†Œ(Element)에 동일 연산을 μ μš©ν•˜μ—¬, λ³€ν˜•λœ μƒˆ μ»¬λ ‰μ…˜ λ°˜ν™˜
rint("\n---------- [ Map ] ----------")

let names = ["Chris", "Alex", "Bob", "Barry"]
names
  .map { $0 + "'s name" } // [String]
  .forEach { print($0) } //forEachμ—°κ²°ν•΄μ„œ μ“Έ 수 있음.


let names2 = names.map { $0 + "'S mame"} // [String]
print(names2) //dlfjstlrdmfheh rksmd


let intArr = Array<Int>(repeating: 2, count: 10)
for (idx, value) in intArr.enumerated() {
  print(idx + value)
}
// 이 값을 μƒˆλ‘œμš΄ λ³€μˆ˜μ— λ‹΄κ³  싢을 λ•Œ


// for문을 μ‚¬μš©ν•œ 방법
var numArr = [Int]()
for (idx, value) in intArr.enumerated() {
  numArr.append(idx + value)
}
print(numArr)

// map을 μ‚¬μš©ν•œ 방법
let indexPlusElement = intArr.enumerated().map {
  return $0 + $1 //$0은 인덱슀 $1은 벨λ₯˜.
}
print(indexPlusElement)

//map:μƒˆλ‘œμš΄ 배열을 λ°˜ν™˜ν•˜λ„λ‘ν•˜λŠ” ν•¨μˆ˜
//map을 μ“°λ©΄ 배열이 μ²˜μŒλΆ€ν„° λκΉŒμ§€ λͺ¨λ‘ 싀행이 λ˜λ©΄μ„œ, μ „λ‹¬λ˜λŠ” 값에 λ”°λΌμ„œ λ°˜ν™˜κ°’μ΄λ‚˜ νƒ€μž…μ΄ 달라짐.  //μ™„μ „νžˆ λ‹€λ₯Έ νƒ€μž…μœΌλ‘œ λ³€ν˜• κ°€λŠ₯.
//intArr.map(<#T##transform: (Int) throws -> T##(Int) throws -> T#>) intλ©΄, mapμž…λ ₯μ‹œ μžλ™μœΌλ‘œ transformκ°’(λ°˜ν™˜νƒ€μž…μ΄)이 (Int)둜 λ³€ν˜•λ˜μ–΄ λ‚˜μ˜΄.
//[""].map(<#T##transform: (String) throws -> T##(String) throws -> T#>) string
  • 1~9κΉŒμ§€μ˜ 숫자λ₯Ό 각각 2λ°°μ”© κ³±ν•œ 배열을 λ°˜ν™˜
Array(1...9)
  .map{ $0 * 2 }

//κ²°κ³Ό : [2,4,6,8,10,12]

filter

  • μ»¬λ ‰μ…˜μ˜ 각 μš”μ†Œλ₯Ό ν‰κ°€ν•˜μ—¬ 쑰건을 λ§Œμ‘±ν•˜λŠ” μš”μ†Œλ§Œμ„ μƒˆλ‘œμš΄ μ»¬λ ‰μ…˜μœΌλ‘œ λ°˜ν™˜
print("\n---------- [ filter ] ----------")

//let names = ["Chris", "Alex", "Bob", "Barry"]

let containBNames = names
  .filter { (name) -> Bool in
    return name.contains("B")
  }
print(containBNames)

names.filter { $0.contains("B") }

let names1 = names.filter { _ in true }
names1


var countAlexName = names.filter { $0 == "Alex" }.count
print(countAlexName)


let anotherNames = ["Alex", "Bob", "Alex", "Alex"]
countAlexName = anotherNames.filter { $0 == "Alex" }.count
print(countAlexName)



// for 문을 μ‚¬μš©ν•˜λŠ” 경우
var count = 0
for name in anotherNames {
  if name == "Alex" {
    count += 1
  }
}
print(count)
  • filterλ₯Ό μ΄μš©ν•΄μ„œ 1~50의 μˆ«μžμ€‘ 3의 배수만 좜λ ₯ν•˜κΈ°
Array(1...50)
  .filter {$0.isMultiple(of: 3) }

reduce

  • μ»¬λ ‰μ…˜μ˜ 각 μš”μ†Œλ“€μ„ κ²°ν•©ν•˜μ—¬ 단 ν•˜λ‚˜μ˜ νƒ€μž…μ„ μ§€λ‹Œ κ°’μœΌλ‘œ λ°˜ν™˜. e.g. Int, String νƒ€μž…
print("\n---------- [ reduce ] ----------\n")

/*
 (1...100)
   .reduce(<#T##initialResult: Result##Result#>, <#T##nextPartialResult: (Result, Int) throws -> Result##(Result, Int) throws -> Result#>)
 
 Result Type - 결과둜 μ–»κ³ μžν•˜λŠ” κ°’μ˜ νƒ€μž…
 
 initialResult - μ΄ˆκΈ°κ°’
 nextPartialResult - (이전 μš”μ†ŒκΉŒμ§€μ˜ κ²°κ³Όκ°’, μ»¬λ ‰μ…˜μ΄ μ§€λ‹Œ ν˜„μž¬ μš”μ†Œ)
 */

let sum1to100 = (1...100).reduce(0) { (sum: Int, next: Int) in
  return sum + next
}
print(sum1to100)
// 0 + 1 = 1
// 1 + 2 = 3
// 3 + 3 = 6
// 6 + 4 = 10
// ....

print((1...100).reduce(0) { $0 + $1 })


// for문을 μ‚¬μš©ν•˜λŠ” 경우
var sum = 0
for i in 1...100 {
  sum += i
}
print(sum)


//직접 λ”ν•˜λŠ” ν˜•νƒœ X
//(1...100).reduce(0) { (sum, next) in
//  sum += next
//}


// λ¬Έμžμ—΄μ„ 숫자둜
["100", "200"].reduce(50) { //μ΄ˆκΉƒκ°’ 50. λ¬Έμžμ—΄ -> μˆ«μžμ—΄
  $0 + Int($1)! // 150 + 200 = 350
}
// 50 + 100 = 150
// 150 + 200 = 350

Question

  • λ¬Έμžμ—΄ 배열을 reduceλ₯Ό μ΄μš©ν•΄ ν•˜λ‚˜μ˜ λ¬Έμžμ—΄λ‘œ ν•©μΉ˜κΈ°
  • 숫자 배열을 reduceλ₯Ό μ΄μš©ν•΄ ν•˜λ‚˜μ˜ λ¬Έμžμ—΄λ‘œ ν•©μΉ˜κΈ°
// μ•„λž˜ λ‘˜ λͺ¨λ‘ reduceλ₯Ό μ΄μš©ν•΄ "123" μ΄λΌλŠ” λ¬Έμžμ—΄μ΄ λ˜λ„λ‘ λ§Œλ“€κΈ°

let merge1 = ["1", "2", "3"].reduce("") { $0 + $1 }
print(merge1)

let merge2 = [1, 2, 3].reduce("") { $0 + String($1) }
print(merge2)

//μœ„ λ‚΄μš©μ„ Full Syntax 둜 ν‘œν˜„ν–ˆμ„ λ•Œ
//[1, 2, 3]
//  .reduce("") { (str: String, num: Int) in
//    return str + String(num)
//  }

compactMap

  • μ»¬λ ‰μ…˜μ˜ 각 μš”μ†Œ(Element)에 동일 연산을 μ μš©ν•˜μ—¬ λ³€ν˜•λœ μƒˆ μ»¬λ ‰μ…˜ λ°˜ν™˜
  • μ˜΅μ…”λ„ 제거
let optionalStringArr = ["A", nil, "B", nil, "C"]
print(optionalStringArr)
print(optionalStringArr.compactMap { $0 })


let numbers = [-2, -1, 0, 1, 2]
let positiveNumbers = numbers.compactMap { $0 >= 0 ? $0 : nil }
print(positiveNumbers)

print(numbers.map { $0 >= 0 ? $0 : nil })

flatMap

  • μ€‘μ²©λœ μ»¬λ ‰μ…˜μ„ ν•˜λ‚˜μ˜ μ»¬λ ‰μ…˜μœΌλ‘œ 병합
let nestedArr: [[Int]] = [[1, 2, 3], [9, 8, 7], [-1, 0, 1]]
print(nestedArr)
print(nestedArr.flatMap { $0 })


let nestedArr2: [[[Int]]] = [[[1, 2], [3, 4], [5, 6]], [[7, 8], [9, 10]]]
let flattenNumbers1 = nestedArr2.flatMap { $0 }
print(flattenNumbers1)

let flattenNumbers2 = flattenNumbers1.flatMap { $0 }
print(flattenNumbers2)

nestedArr2
  .flatMap { $0 }
  .flatMap { $0 }

정리

β€’ forEach

  • μ»¬λ ‰μ…˜μ˜ 각 μš”μ†Œ(Element)에 동일 연산을 μ μš©ν•˜λ©°, λ°˜ν™˜κ°’μ΄ μ—†λŠ” ν˜•νƒœ

β€’ map

  • μ»¬λ ‰μ…˜μ˜ 각 μš”μ†Œ(Element)에 동일 연산을 μ μš©ν•˜μ—¬, λ³€ν˜•λœ μƒˆ μ»¬λ ‰μ…˜ λ°˜ν™˜

β€’ filter

  • μ»¬λ ‰μ…˜μ˜ 각 μš”μ†Œλ₯Ό ν‰κ°€ν•˜μ—¬ 쑰건을 λ§Œμ‘±ν•˜λŠ” μš”μ†Œλ§Œμ„ μƒˆλ‘œμš΄ μ»¬λ ‰μ…˜μœΌλ‘œ λ°˜ν™˜

β€’ reduce

  • μ»¬λ ‰μ…˜μ˜ 각 μš”μ†Œλ“€μ„ κ²°ν•©ν•˜μ—¬ 단일 νƒ€μž…μœΌλ‘œ λ°˜ν™˜. e.g. Int, String

β€’ flatMap

  • μ€‘μ²©λœ μ»¬λ ‰μ…˜μ„ ν•˜λ‚˜μ˜ μ»¬λ ‰μ…˜μœΌλ‘œ 병합

β€’ compactMap

  • μ»¬λ ‰μ…˜μ˜ μš”μ†Œ 쀑 μ˜΅μ…”λ„μ΄ μžˆμ„ 경우 제거
  • (flatMap으둜 μ‚¬μš©ν•˜λ‹€κ°€ Swift 4.1 μ—μ„œ compactMap 으둜 변경됨)

Practice 1

Input : myPet λ°°μ—΄ 이용

[ 1번 문제 ]
Pet νƒ€μž…μ˜ 배열을 νŒŒλΌλ―Έν„°λ‘œ λ°›μ•„ κ·Έ 배열에 ν¬ν•¨λœ Pet 쀑
κ°•μ•„μ§€μ˜ λ‚˜μ΄λ§Œμ„ ν•©μ‚°ν•œ κ²°κ³Όλ₯Ό λ°˜ν™˜ν•˜λŠ” sumDogAge ν•¨μˆ˜ κ΅¬ν˜„
func sumDogAge(pets: [Pet]) -> Int

[ 2번 문제 ]
Pet νƒ€μž…μ˜ 배열을 νŒŒλΌλ―Έν„°λ‘œ λ°›μ•„ λͺ¨λ“  Pet이 λ‚˜μ΄λ₯Ό 1μ‚΄μ”© 더 λ¨Ήμ—ˆμ„ λ•Œμ˜
μƒνƒœλ₯Ό μ§€λ‹Œ μƒˆλ‘œμš΄ 배열을 λ°˜ν™˜ν•˜λŠ” oneYearOlder ν•¨μˆ˜ κ΅¬ν˜„
func oneYearOlder(of pets: [Pet]) -> [Pet]
print("\n---------- [ Practice 1 ] ----------\n")

struct Pet {
  enum PetType {
    case dog, cat, snake, pig, bird
  }
  var type: PetType
  var age: Int
}

let myPet = [
  Pet(type: .dog, age: 13),
  Pet(type: .dog, age: 2),
  Pet(type: .dog, age: 7),
  Pet(type: .cat, age: 9),
  Pet(type: .snake, age: 4),
  Pet(type: .pig, age: 5),
]
  • 1번 문제
//case1

//func sumDogAge(pets: [Pet]) -> Int {
//var ageSum = 0
//for pet in pets {
//guard pet.type == .dog else { continue }
//ageSum += pet.age
//}
//return ageSum
//}

//case2
func sumDogAge(pets: [Pet]) -> Int {
  return pets
    .filter{ $0.type == .dog}
    .reduce(0) {$0 + $1.age}
  sumDogAge(pets: myPet)
  
  • 2번 문제
//case1
  
  //  func oneYearOlder(of pets: [Pet]) -> [Pet] {
  //  var oneYearOlderPets = [Pet]()
  //  for pet in pets {
  //  let temp = Pet(type: pet.type, age: pet.age + 1)
  //  oneYearOlderPets.append(temp)
  //  }
  //  return oneYearOlderPets
  //  }

  //case 2  
  func oneYearOlder(of pets: [Pet]) -> [Pet] {
    return pets.map {
      Pet(type: $0.type, age: $0.age + 1)
    }
  }

Practice 2

let immutableArray = Array(1...40)

[ 문제 ]
immutableArray λ°°μ—΄μ˜ 각 μΈλ±μŠ€μ™€ ν•΄λ‹Ή 인덱슀의 μš”μ†Œλ₯Ό κ³±ν•œ κ°’ 쀑
ν™€μˆ˜λŠ” μ œμ™Έν•˜κ³  μ§μˆ˜μ— λŒ€ν•΄μ„œλ§Œ λͺ¨λ“  값을 λ”ν•˜μ—¬ κ²°κ³Ό 좜λ ₯

단, μ•„λž˜ 1 ~ 3λ²ˆμ— ν•΄λ‹Ήν•˜λŠ” ν•¨μˆ˜λ₯Ό 각각 μ •μ˜ν•˜κ³ 
이것듀을 ν•¨κ»˜ μ‘°ν•©ν•˜μ—¬ μœ„ 문제의 κ²°κ³Όλ₯Ό λ„μΆœν•  것
1. λ°°μ—΄μ˜ 각 μš”μ†Œ * index 값을 λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜
2. 짝수 μ—¬λΆ€λ₯Ό νŒλ³„ν•˜λŠ” ν•¨μˆ˜
3. 두 개의 숫자λ₯Ό λ”ν•˜μ—¬ λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜
let immutableAttay = Array(1...40)

//1.
func multiplyByIndex(index: Int, number: Int) -> Int {
return index * number
}
//2.
func isEven(number: Int) -> Bool {
return number & 1 == 0
}
//3.
func addTwoNumbers(lhs: Int, rhs: Int) -> Int {
return lhs + rhs
  
}
  • κ³ μ°¨ν•¨μˆ˜ μ‚¬μš©ν•˜μ§€ μ•Šμ€ 예제
var sum = 0
for (index, num) in immutableArray.enumerated() {
let multipliedNum = multiplyByIndex(index: index, number: num)

if isEven(number: multipliedNum) {
sum = addTwoNumbers(lhs: sum, rhs: multipliedNum)
}
}
  • κ³ μ°¨ν•¨μˆ˜ μ‚¬μš© (Function as argument)
immutableArray.enumerated()
.map(multiplyByIndex(index:number:))
.filter(isEven(number:))
.reduce(0, addTwoNumbers(lhs:rhs:))
  • Closures 이용
immutableArray.enumerated()
.map { (offset, element) -> Int in
return offset * element
}.filter { (element) -> Bool in
return element & 1 == 0
}.reduce(0) { (sum, nextElement) -> Int in
return sum + nextElement
}
  • 더 쀄일 수 있음(Shorthand Argument Names)
immutableArray.enumerated()
.map { $0 * $1 }
.filter { $0 & 1 == 0 }
.reduce(0) { $0 + $1}

//map, reduceλ₯Ό 쀄일 수 μžˆλŠ” 이유. 
//무쑰건 2κ°œμΌλ•Œ κ°€λŠ₯.
// 2개의 값을 λ”ν•˜λŠ” 것. 
  • 더 쀄일 수 있음(Shorthand Argument Names)
immutableArray.enumerated()
.map(*)
.filter({ $0 & 1 == 0 })
.reduce(0, +)

map vs compactMap

  • μ‹¬ν™”μ˜ˆμ œ
let array = ["1j", "2d", "3", "4"]

let m1 = array.map({ Int($0) })
let f1 = array.compactMap({ Int($0) })

let m2 = array.map({ Int($0) })[0]
let f2 = array.compactMap({ Int($0) })[0]
  • map - stdlib/public/core/Sequence.swift

  • filter - stdlib/public/core/Sequence.swift

  • swift/stdlib/public/core/FlatMap.swift