Swift

[Swift] λͺ¨λ‚˜λ“œ : μ»¨ν…μŠ€νŠΈ, ν•¨μˆ˜κ°μ²΄, λͺ¨λ‚˜λ“œ

yevdev 2022. 3. 24. 16:17

πŸ’‘ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ—μ„œμ˜ λͺ¨λ‚˜λ“œ?

- μˆœμ„œκ°€ μžˆλŠ” 연산을 μ²˜λ¦¬ν•  λ•Œ 자주 ν™œμš©ν•˜λŠ” λ””μžμΈ νŒ¨ν„΄

 

πŸ’‘ν”„λ‘œκ·Έλž˜λ°μ—μ„œ λͺ¨λ‚˜λ“œκ°€ κ°–μΆ°μ•Ό ν•  쑰건

1. νƒ€μž…μ„ 인자둜 λ°›λŠ” νƒ€μž…(νŠΉμ • νƒ€μž…μ˜ 값을 포μž₯)

2. νŠΉμ • νƒ€μž…μ˜ 값을 포μž₯ν•œ 것을 λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜(λ§€μ„œλ“œ)κ°€ 쑴재

3. 포μž₯된 값을 λ³€ν™˜ν•˜μ—¬ 같은 ν˜•νƒœλ‘œ 포μž₯ν•˜λŠ” ν•¨μˆ˜(λ§€μ„œλ“œ)κ°€ 쑴재

 

크게 3가지 κ°œλ…μ„ μ•Œμ•„λ³΄κ²Œ 될 것이닀.

1️⃣ μ»¨ν…μŠ€νŠΈ

2️⃣ ν•¨μˆ˜κ°μ²΄

3️⃣ λͺ¨λ‚˜λ“œ

 

 

1️⃣ μ»¨ν…μŠ€νŠΈ

μ»¨ν…μŠ€νŠΈ = μ»΅

μ½˜ν…μΈ  = μ»΅ μ•ˆμ— λ‹΄κ²¨μžˆλŠ” λ¬Ό 

 

즉, μ»¨ν…μŠ€νŠΈλŠ” μ–΄λ–€ μœ„μΉ˜μ— 값이 μ‘΄μž¬ν•  수 μžˆλŠ” λ§₯락 μ΄λΌκ³  λ³Ό 수 μžˆλ‹€.

μ—¬κΈ°μ„œ μš°λ¦¬λŠ” Optional 을 μ‰½κ²Œ λ– μ˜¬λ¦΄ 수 μžˆλ‹€. Optional은 값이 μžˆμ„ μˆ˜λ„, 없을 μˆ˜λ„ 있음!!

 

Optional

λͺ¨λ‚˜λ“œ 쑰건 1 만쑱 : Optional은 Wrapped νƒ€μž…μ„ 인자둜 λ°›λŠ” (μ œλ„€λ¦­) νƒ€μž…μ΄λ‹€.

λͺ¨λ‚˜λ“œ 쑰건 2 만쑱 : Optional νƒ€μž…μ€ Optional<Int>.init(2) 처럼 λ‹€λ₯Έ νƒ€μž…(Int)의 값을 κ°–λŠ” μƒνƒœμ˜ μ»¨ν…μŠ€νŠΈλ₯Ό 생성할 수 μžˆλ‹€.

λͺ¨λ‚˜λ“œ 쑰건 3을 λ§Œμ‘±ν•˜λŠ” μ§€λŠ” 이제 천천히 μ•Œμ•„λ³΄μž.

 

addThree ν•¨μˆ˜

func addThree(_ num: Int) -> Int { 
	return num + 3
}

addThree(Optional(2))   // 였λ₯˜ λ°œμƒ!

Int νƒ€μž…μ˜ 값을 전달받아 3을 λ”ν•˜μ—¬ λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜μ— μ˜΅μ…”λ„μ„ μ „λ‹¬μΈμžλ‘œ μ‚¬μš©ν•˜λ©΄ 였λ₯˜κ°€ λ‚œλ‹€.

μ™œ? μˆœμˆ˜ν•œ 값이 μ•„λ‹Œ μ˜΅μ…”λ„μ΄λΌλŠ” μ»¨ν…μŠ€νŠΈλ‘œ λ‘˜λŸ¬μ‹Έμ—¬ μ „λ‹¬λ˜κΈ° λ•Œλ¬Έμ΄λ‹€.

 

 

➰ ν•¨μˆ˜ 객체λ₯Ό μ•Œμ•„λ³΄κΈ° μ „, 맡에 λŒ€ν•΄ μ•Œμ•„λ³΄μž.

μœ„μ—μ„œ λ³Έ κ²ƒμ²˜λŸΌ μ˜΅μ…”λ„μ€ μ»¨ν…Œμ΄λ„ˆμ™€ 값을 κ°–κΈ° λ•Œλ¬Έμ— 맡 ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€.

-> 맡을 μ‚¬μš©ν•˜λ©΄ μ»¨ν…Œμ΄λ„ˆ μ•ˆμ˜ 값을 μ²˜λ¦¬ν•  수 μžˆλ‹€.

 

맡 λ§€μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ μ˜΅μ…”λ„μ„ μ—°μ‚°ν•  수 μžˆλŠ” addThree(_:) ν•¨μˆ˜

Optional(2).map(addThree)	// Optional(5)

μ˜΅μ…”λ„μ— 맡 λ©”μ„œλ“œμ™€ ν΄λ‘œμ €μ˜ μ‚¬μš©

var value: Int? = 2
value.map{$0+2}
print(value)	// Optional(2)
value = nil
value.map{$0+2}
print(value)	// nil

 

 

2️⃣ ν•¨μˆ˜ 객체

πŸ’‘ν•¨μˆ˜ κ°μ²΄λž€? 맡을 μ μš©ν•  수 μžˆλŠ” μ»¨ν…Œμ΄λ„ˆ νƒ€μž…

 

Optional(2).map(addThree) 둜 ν•¨μˆ˜ 객체와 맡 λ©”μ„œλ“œμ˜ λ™μž‘μ„ μžμ„Ένžˆ μ•Œμ•„λ³΄μž.

1. 맡이 ν•¨μˆ˜λ₯Ό 인자둜 λ°›λŠ”λ‹€. : map(addThree)

2. ν•¨μˆ˜κ°μ²΄μ— 맡이 전달받은 ν•¨μˆ˜λ₯Ό 적용 : Optional(2)

3. μƒˆλ‘œμš΄ ν•¨μˆ˜ 객체λ₯Ό λ°˜ν™˜ : Optional(5)

 

μ˜΅μ…”λ„μ˜ map λ©”μ„œλ“œ κ΅¬ν˜„

extension Optional {
    func map<U>(f: (Wrapped) -> U) -> U {
        switch self {
        case .some(let x): return f(x)
        case .none: return .none
        }
    }
}

-> switch λ¬Έμ—μ„œ λ§Œμ•½ 값이 μžˆλ‹€λ©΄, 전달받은 ν•¨μˆ˜μ— μžμ‹ μ˜ 값을 μ μš©ν•œ 결괏값을 λ‹€μ‹œ μ»¨ν…μŠ€νŠΈμ— λ„£μ–΄ λ°˜ν™˜

-> 값이 μ—†λ‹€λ©΄, ν•¨μˆ˜λ₯Ό μ‹€ν–‰ν•˜μ§€ μ•Šκ³  빈 μ»¨ν…μŠ€νŠΈλ₯Ό λ°˜ν™˜ν•œλ‹€.

 

 

 

3️⃣ λͺ¨λ‚˜λ“œ

πŸ’‘λ‹«νžŒ ν•¨μˆ˜κ°μ²΄?

- ν•¨μˆ˜κ°μ²΄ μ€‘μ—μ„œ μžμ‹ μ˜ μ»¨ν…μŠ€νŠΈμ™€ 같은 μ»¨ν…μŠ€νŠΈμ˜ ν˜•νƒœλ‘œ 맡핑할 수 μžˆλŠ” ν•¨μˆ˜κ°μ²΄

- λͺ¨λ‚˜λ“œλŠ” λ‹«νžŒ ν•¨μˆ˜κ°μ²΄μ΄λ‹€!

 

πŸ’‘ν”Œλž«λ§΅?

- λͺ¨λ‚˜λ“œκ°€ 맡핑을 μˆ˜ν–‰ν•  λ•Œ ν•„μš”ν•œ λ©”μ„œλ“œ

 

ν”Œλž«λ§΅μ€ 맡과 같이 ν•¨μˆ˜λ₯Ό λ§€κ°œλ³€μˆ˜λ‘œ λ°›κ³ , μ˜΅μ…”λ„μ€ λͺ¨λ‚˜λ“œμ΄λ―€λ‘œ ν”Œλž«λ§΅μ„ μ‚¬μš©ν•  수 μžˆλ‹€.

 

짝수면 2λ₯Ό κ³±ν•΄μ„œ λ°˜ν™˜ν•˜κ³  μ§μˆ˜κ°€ μ•„λ‹ˆλΌλ©΄ nil을 λ°˜ν™˜ν•˜λŠ” ν•¨μˆ˜ doubleEven(_:)

doubleEven(_:) ν•¨μˆ˜μ™€ ν”Œλž«λ§΅μ˜ μ‚¬μš©

func doubleEven(_ num: Int) -> Int? {
    if num.isMultiple(of: 2){
        return num * 2
    }
    return nil
}

Optional(3).flatMap(doubleEven)
// nil (== Optional<Int>.none)

Optional(3).flatMap(doubleEven)의 λ™μž‘

1. μ»¨ν…μŠ€νŠΈλ‘œλΆ€ν„° κ°’ μΆ”μΆœ (3)

2. μΆ”μΆœν•œ 값을 doubleEven ν•¨μˆ˜μ— 전달

3. 빈 μ»¨ν…μŠ€νŠΈλ‘œ λ°˜ν™˜

Optional.none.flatMap(doubleEven)의 λ™μž‘

1. 빈 μ»¨ν…μŠ€νŠΈ

2. ν”Œλž«λ§΅μ€ 아무것도 ν•˜μ§€ μ•ŠμŒ

3. κ²°κ΅­ λ‹€μ‹œ 빈 μ»¨ν…μŠ€νŠΈ λ°˜ν™˜

 

➰map(_:)과 flatMap(_:)의 차이

- ν”Œλž«λ§΅μ€ 맡과 달리 μ»¨ν…μŠ€νŠΈ λ‚΄λΆ€μ˜ μ»¨ν…μŠ€νŠΈλ₯Ό λͺ¨λ‘ 같은 μœ„μƒμœΌλ‘œ ν‰ν‰ν•˜κ²Œ νŽΌμ³μ€€λ‹€λŠ” 차이가 μžˆλ‹€. 즉, 포μž₯된 κ°’ λ‚΄λΆ€μ˜ 포μž₯된 κ°’μ˜ 포μž₯을 ν’€μ–΄μ„œ 같은 μœ„μƒμœΌλ‘œ νŽΌμ³μ€€λ‹€.

 

➰compactMap(_:) ? 

Optional νƒ€μž…μ— μ‚¬μš©ν•˜μ˜€λ˜ flatMap(_:) λ©”μ„œλ“œλ₯Ό Sequence νƒ€μž…μ΄ Optional νƒ€μž…μ˜ Elementλ₯Ό 포μž₯ν•œ κ²½μš°μ— compactMap(_:)μ΄λ¦„μœΌλ‘œ μ‚¬μš©ν•œλ‹€.

 

맡과 컴팩트의 차이

let optionals: [Int?] = [1, 2, nil, 5]

let mapped: [Int?] = optionals.map{$0}
let compactMapped: [Int] = optionals.compactMap{$0}

print(mapped)
print(compactMapped)

// [Optional(1), Optional(2), nil, Optional(5)]
// [1, 2, 5]

optionalsλŠ” ArrayλΌλŠ” μ»¨ν…Œμ΄λ„ˆμ˜ 내뢀에 Optionalμ΄λΌλŠ” ν˜•νƒœμ˜ μ»¨ν…Œμ΄λ„ˆλ“€μ΄ μ—¬λŸ¬κ°œ λ“€μ–΄κ°€ μžˆλŠ” ν˜•νƒœμ΄λ‹€.

1. 맡 λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•œ κ²°κ³Ό : Array μ»¨ν…Œμ΄λ„ˆ λ‚΄λΆ€μ˜ κ°’ νƒ€μž…μ΄λ‚˜ ν˜•νƒœκ°€ μ–΄μ°Œ λ˜μ—ˆλ“ , Array 내뢀에 값이 있으면 κ·Έ 값을 κ·Έμ € ν΄λ‘œμ €μ˜ μ½”λ“œμ—μ„œλ§Œ μ‹€ν–‰ν•˜κ³  κ²°κ³Όλ₯Ό λ‹€μ‹œ Array μ»¨ν…Œμ΄λ„ˆμ— λ‹΄κΈ°λ§Œ ν•œλ‹€.

2. ν”Œλž«λ§΅μ„ 톡해 ν΄λ‘œμ €λ₯Ό μ‹€ν–‰ν•œ κ²°κ³Ό : μ•Œμ•„μ„œ λ‚΄λΆ€μ˜ μ»¨ν…Œμ΄λ„ˆκΉŒμ§€ 값을 μΆ”μΆœν•œλ‹€. 

-> κ·Έλ ‡κΈ° λ•Œλ¬Έμ— mappedλŠ” λ‹€μ‹œ [Int?] νƒ€μž…μ΄ 되며, compactMappedλŠ” [Int] νƒ€μž…μ΄ λœλ‹€.

 

μ€‘μ²©λœ μ»¨ν…Œμ΄λ„ˆμ—μ„œ 맡과 ν”Œλž«λ§΅(콀팩트맡)의 차이

let multipleContainer = [[1, 2, Optional.none], [3, Optional.none], [4, 5, Optional.none]]

let mappedMultipleContainer = multipleContainer.map{ $0.map{ $0 }}
let flatmappedMultipleContainer = multipleContainer.flatMap{ $0.compactMap{ $0 }}

print(mappedMultipleContainer)
print(flatmappedMultipleContainer)

// [[Optional(1), Optional(2), nil], [Optional(3), nil], [Optional(4), Optional(5), nil]]
// [1, 2, 3, 4, 5]

 

ν”Œλž«λ§΅μ˜ ν™œμš© (Int νƒ€μž…μ„ String νƒ€μž…μœΌλ‘œ String νƒ€μž…μ„ Int νƒ€μž…μœΌλ‘œ)

func stringToInteger(_ string: String) -> Int? {
    return Int(string)
}

func integerToString(_ integer: Int) -> String? {
    return "\(integer)"
}

var optionalString: String? = "2"

let flattenResult = optionalString.flatMap(stringToInteger)
    .flatMap(integerToString)
    .flatMap(stringToInteger)

print(flattenResult)    // Optional(2)

let mappedResult = optionalString.map(stringToInteger)  // 더이상 체인 μ—°κ²° λΆˆκ°€
print(mappedResult) // Optional(Optional(2))

- String νƒ€μž…μ„ Int νƒ€μž…μœΌλ‘œ λ³€ν™˜ν•˜λŠ” 것은 μ‹€νŒ¨ν•  κ°€λŠ₯성을 λ‚΄ν¬ν•΄μ„œ 결괏값을 μ˜΅μ…”λ„ νƒ€μž…μœΌλ‘œ λ°˜ν™˜

- Int νƒ€μž…μ„ String νƒ€μž…μœΌλ‘œ λ³€ν™˜ν•˜λŠ” 것은 μ‹€νŒ¨ κ°€λŠ₯성은 μ—†μ§€λ§Œ 예λ₯Ό λ“€κ³ μž μ˜΅μ…”λ„ νƒ€μž…μœΌλ‘œ λ°˜ν™˜

- ν”Œλž«λ§΅μ„ μ‚¬μš©ν•˜μ—¬ 체인을 μ—°κ²°ν–ˆμ„ λ•Œ κ²°κ³ΌλŠ” μ˜΅μ…”λ„

- 맡을 μ‚¬μš©ν•˜μ—¬ 체인을 μ—°κ²°ν–ˆμ„ λ•Œ κ²°κ³ΌλŠ” μ˜΅μ…”λ„μ˜ μ˜΅μ…”λ„

-> 이유 : ν”Œλž«λ§΅μ€ ν•¨μˆ˜μ˜ 결괏값에 값이 μžˆλ‹€λ©΄ μΆ”μΆœν•΄μ„œ ν‰ν‰ν•˜κ²Œ λ§Œλ“œλŠ” 과정을 λ‚΄ν¬ν•˜κ³ , 맡은 그렇지 μ•ŠκΈ° λ•Œλ¬Έμ΄λ‹€.

즉, ν”Œλž«λ§΅μ€ 항상 같은 μ»¨ν…μŠ€νŠΈλ₯Ό μœ μ§€ν•  수 μžˆμœΌλ―€λ‘œ 이같은 연쇄 연산도 κ°€λŠ₯ν•˜λ‹€.

 

 


Reference

Swift μŠ€μœ„ν”„νŠΈ ν”„λ‘œκ·Έλž˜λ° - μ•Όκ³°