Ver3.0 ๐ฏ ์ค์ํํธ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ์์ ์ ๋ณต

๐ฏ ์ค์ํํธ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ์์ ์ ๋ณต
๋ถ๋ณ์ฑ๊ณผ ์์ํจ์๋ก ๋ง๋๋ ์์ ํ๊ณ ์์ธก ๊ฐ๋ฅํ ์ฝ๋์ ์ธ๊ณ โจ
์๋ ! ์ค๋์ ์ค์ํํธ์์ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ์ด๋ป๊ฒ ํ์ฉํ๋์ง, ๊ทธ๋ฆฌ๊ณ ๋ถ๋ณ์ฑ๊ณผ ์์ํจ์ ํจํด์ด ์ ์ค์ํ์ง ํจ๊ป ํํค์ณ๋ณผ ๊ฑฐ์ผ. ๐
ํน์ ์ฝ๋๋ฅผ ์์ฑํ๋ค๊ฐ "์ด? ์ด ๋ณ์ ๊ฐ์ด ์ ๋ฐ๋์์ง?" ํ๋ฉด์ ๋๋ฒ๊น ์ ๋ช ์๊ฐ์ ๋ ๋ ค๋ณธ ๊ฒฝํ ์์ด? ์๋๋ฉด ํจ์๋ฅผ ํธ์ถํ๋๋ฐ ์์์น ๋ชปํ ๋ถ์์ฉ(side effect)์ด ๋ฐ์ํด์ ๋จธ๋ฆฌ๋ฅผ ์ฅ์ด๋ฏ์ ์ ์? ๊ทธ๋ ๋ค๋ฉด ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ด ๋์๊ฒ ๋ฑ ๋ง๋ ํด๊ฒฐ์ฑ ์ด ๋ ์ ์์ด!
ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ๋จ์ํ ํธ๋ ๋ํ ํ๋ก๊ทธ๋๋ฐ ํจ๋ฌ๋ค์์ด ์๋๋ผ, ์ฝ๋์ ์์ ์ฑ๊ณผ ์์ธก ๊ฐ๋ฅ์ฑ์ ๊ทน๋ํํ๋ ์ค์ฉ์ ์ธ ์ ๊ทผ ๋ฐฉ์์ด์ผ. ํนํ ์ค์ํํธ๋ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ์ง์ํ๋ ๋ค์ํ ๊ธฐ๋ฅ๋ค์ ์ธ์ด ์ฐจ์์์ ์ ๊ณตํ๊ณ ์์ด์, ์ด๋ฅผ ์ ํ์ฉํ๋ฉด ์ ๋ง ๊น๋ํ๊ณ ์ ์ง๋ณด์ํ๊ธฐ ์ข์ ์ฝ๋๋ฅผ ์์ฑํ ์ ์์ง. ๐
๐ค ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ด ๋ญ๊ธธ๋?
ํจ์ํ ํ๋ก๊ทธ๋๋ฐ(Functional Programming, FP)์ ํ๋ก๊ทธ๋จ์ ์ํ์ ํจ์์ ์กฐํฉ์ผ๋ก ๋ฐ๋ผ๋ณด๋ ํ๋ก๊ทธ๋๋ฐ ํจ๋ฌ๋ค์์ด์ผ. ์ฌ๊ธฐ์ ํต์ฌ์ "์์ํจ์"์ "๋ถ๋ณ์ฑ"์ด๋ผ๋ ๋ ๊ฐ์ง ๊ฐ๋ ์ด์ง.
๐ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ํต์ฌ ์์น
1. ์์ํจ์ (Pure Functions)
๊ฐ์ ์
๋ ฅ์ ๋ํด ํญ์ ๊ฐ์ ์ถ๋ ฅ์ ๋ฐํํ๊ณ , ์ธ๋ถ ์ํ๋ฅผ ๋ณ๊ฒฝํ์ง ์๋ ํจ์
2. ๋ถ๋ณ์ฑ (Immutability)
ํ ๋ฒ ์์ฑ๋ ๋ฐ์ดํฐ๋ ๋ณ๊ฒฝ๋์ง ์๊ณ , ํ์ํ๋ฉด ์๋ก์ด ๋ฐ์ดํฐ๋ฅผ ์์ฑ
3. ์ผ๊ธ ํจ์ (First-Class Functions)
ํจ์๋ฅผ ๋ณ์์ ํ ๋นํ๊ณ , ์ธ์๋ก ์ ๋ฌํ๊ณ , ๋ฐํ๊ฐ์ผ๋ก ์ฌ์ฉ ๊ฐ๋ฅ
4. ๊ณ ์ฐจ ํจ์ (Higher-Order Functions)
ํจ์๋ฅผ ์ธ์๋ก ๋ฐ๊ฑฐ๋ ํจ์๋ฅผ ๋ฐํํ๋ ํจ์
์ค์ํํธ๋ ์ด ๋ชจ๋ ์์น์ ์ง์ํ๋ ๋ฉํฐ ํจ๋ฌ๋ค์ ์ธ์ด์ผ. ๊ฐ์ฒด์งํฅ ํ๋ก๊ทธ๋๋ฐ๋ ํ ์ ์๊ณ , ํจ์ํ ํ๋ก๊ทธ๋๋ฐ๋ ํ ์ ์์ง. ์ด๊ฒ ์ค์ํํธ์ ํฐ ์ฅ์ ์ด์ผ! ๐
๐ก ์ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ๋ฐฐ์์ผ ํ ๊น?
โข ๋ฒ๊ทธ ๊ฐ์: ์ํ ๋ณ๊ฒฝ์ด ์์ผ๋ ์์ธก ๋ถ๊ฐ๋ฅํ ๋ฒ๊ทธ๊ฐ ์ค์ด๋ค์ด
โข ํ
์คํธ ์ฉ์ด์ฑ: ์์ํจ์๋ ํ
์คํธํ๊ธฐ ์ ๋ง ์ฌ์
โข ๋ณ๋ ฌ ์ฒ๋ฆฌ: ์ํ ๊ณต์ ๊ฐ ์์ด์ ๋ฉํฐ์ค๋ ๋ฉ์ด ์์ ํด
โข ์ฝ๋ ์ฌ์ฌ์ฉ์ฑ: ์์ ํจ์๋ค์ ์กฐํฉํด์ ๋ณต์กํ ๋ก์ง์ ๋ง๋ค ์ ์์ด
โข ๊ฐ๋
์ฑ: ์ ์ธ์ ์ฝ๋ ์คํ์ผ๋ก ์๋๊ฐ ๋ช
ํํด์ ธ
๐ ์์ํจ์๋ ๋ฌด์์ธ๊ฐ?
์์ํจ์๋ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ๊ฐ์ฅ ๊ธฐ๋ณธ์ด ๋๋ ๊ฐ๋ ์ด์ผ. ๊ฐ๋จํ๊ฒ ๋งํ๋ฉด, ์ธ๋ถ ์ธ๊ณ์ ๋จ์ ๋ ๋ ๋ฆฝ์ ์ธ ํจ์๋ผ๊ณ ์๊ฐํ๋ฉด ๋ผ.
์์ํจ์์ ๋ ๊ฐ์ง ์กฐ๊ฑด:
1. ์ฐธ์กฐ ํฌ๋ช
์ฑ (Referential Transparency)
๊ฐ์ ์
๋ ฅ์ ๋ํด ํญ์ ๊ฐ์ ์ถ๋ ฅ์ ๋ฐํํด์ผ ํด. ํจ์ ํธ์ถ์ ๊ทธ ๊ฒฐ๊ณผ๊ฐ์ผ๋ก ๋์ฒดํด๋ ํ๋ก๊ทธ๋จ์ ๋์์ด ๋ณํ์ง ์์์ผ ํ์ง.
2. ๋ถ์์ฉ ์์ (No Side Effects)
ํจ์ ์ธ๋ถ์ ์ํ๋ฅผ ๋ณ๊ฒฝํ์ง ์์์ผ ํด. ์ ์ญ ๋ณ์๋ฅผ ์์ ํ๊ฑฐ๋, ํ์ผ์ ์ฐ๊ฑฐ๋, ๋คํธ์ํฌ ์์ฒญ์ ํ๊ฑฐ๋, ํ๋ฉด์ ์ถ๋ ฅํ๋ ๋ฑ์ ํ์๊ฐ ์์ด์ผ ํด.
โ ์์ํจ์ ์์
// ์์ํจ์ - ๊ฐ์ ์
๋ ฅ์ ํญ์ ๊ฐ์ ์ถ๋ ฅ
func add(_ a: Int, _ b: Int) -> Int {
return a + b
}
// ์์ํจ์ - ์ธ๋ถ ์ํ์ ์์กดํ์ง ์์
func multiply(_ numbers: [Int], by factor: Int) -> [Int] {
return numbers.map { $0 * factor }
}
// ์์ํจ์ - ๋ถ๋ณ ๋ฐ์ดํฐ ๊ตฌ์กฐ ์ฌ์ฉ
func updateUser(name: String, in user: User) -> User {
return User(name: name, age: user.age, email: user.email)
}
// ์ฌ์ฉ ์์
let result1 = add(3, 5) // ํญ์ 8
let result2 = add(3, 5) // ํญ์ 8
print(result1 == result2) // true
let numbers = [1, 2, 3, 4, 5]
let doubled = multiply(numbers, by: 2)
print(numbers) // [1, 2, 3, 4, 5] - ์๋ณธ ์ ์ง
print(doubled) // [2, 4, 6, 8, 10] - ์๋ก์ด ๋ฐฐ์ด
โ ์์ํ์ง ์์ ํจ์ ์์
// ๋น์์ํจ์ - ์ ์ญ ์ํ์ ์์กด
var globalCounter = 0
func incrementCounter() -> Int {
globalCounter += 1 // ์ธ๋ถ ์ํ ๋ณ๊ฒฝ!
return globalCounter
}
// ๋น์์ํจ์ - ์ธ๋ถ ์ํ ๋ณ๊ฒฝ
var users: [User] = []
func addUser(_ user: User) {
users.append(user) // ์ธ๋ถ ๋ฐฐ์ด ๋ณ๊ฒฝ!
}
// ๋น์์ํจ์ - ๋๋ค ๊ฐ ๋ฐํ
func getRandomNumber() -> Int {
return Int.random(in: 1...100) // ๋งค๋ฒ ๋ค๋ฅธ ๊ฐ!
}
// ๋น์์ํจ์ - ํ์ฌ ์๊ฐ ์์กด
func getCurrentTimestamp() -> Date {
return Date() // ํธ์ถ ์์ ๋ง๋ค ๋ค๋ฅธ ๊ฐ!
}
// ๋ฌธ์ ์ ํ์ธ
let count1 = incrementCounter() // 1
let count2 = incrementCounter() // 2 - ๊ฐ์ ์
๋ ฅ(์์)์ธ๋ฐ ๋ค๋ฅธ ์ถ๋ ฅ!
print(count1 == count2) // false - ์์ธก ๋ถ๊ฐ๋ฅ!
๐ ์์ํจ์๋ก ๋ฆฌํฉํ ๋งํ๊ธฐ
๋น์์ํจ์๋ฅผ ์์ํจ์๋ก ๋ฐ๊พธ๋ ํต์ฌ์ ์ธ๋ถ ์ํ๋ฅผ ์ธ์๋ก ๋ฐ๊ณ , ์๋ก์ด ์ํ๋ฅผ ๋ฐํํ๋ ๊ฒ์ด์ผ!
// ์์ํจ์๋ก ๋ณํ
func increment(_ counter: Int) -> Int {
return counter + 1
}
func addUser(_ user: User, to users: [User]) -> [User] {
var newUsers = users
newUsers.append(user)
return newUsers
}
// ๋ ๋์ ๋ฐฉ๋ฒ - ๋ฐฐ์ด ๋ณต์ฌ ์์ด
func addUser(_ user: User, to users: [User]) -> [User] {
return users + [user]
}
// ์ฌ์ฉ ์์
var counter = 0
counter = increment(counter) // 1
counter = increment(counter) // 2
var userList: [User] = []
userList = addUser(newUser, to: userList)
// ์๋ณธ userList๋ ๋ณ๊ฒฝ๋์ง ์๊ณ , ์๋ก์ด ๋ฐฐ์ด์ด ๋ฐํ๋จ
์์ํจ์์ ๊ฐ์ฅ ํฐ ์ฅ์ ์ ํ ์คํธํ๊ธฐ ์ฝ๋ค๋ ๊ฑฐ์ผ. ์ธ๋ถ ์ํ๋ฅผ ์ ๊ฒฝ ์ธ ํ์ ์์ด, ์ ๋ ฅ๊ณผ ์ถ๋ ฅ๋ง ํ์ธํ๋ฉด ๋๊ฑฐ๋ ! ๐ฏ
๐ ๋ถ๋ณ์ฑ(Immutability)์ ํ
๋ถ๋ณ์ฑ์ ํ ๋ฒ ์์ฑ๋ ๋ฐ์ดํฐ๋ ์ ๋ ๋ณ๊ฒฝ๋์ง ์๋๋ค๋ ์์น์ด์ผ. ์ฒ์์๋ ๋นํจ์จ์ ์ผ๋ก ๋ณด์ผ ์ ์์ง๋ง, ์ค์ ๋ก๋ ์์ฒญ๋ ์ด์ ์ ๊ฐ์ ธ๋ค์ค.
๐ ๋ถ๋ณ์ฑ์ ์ฅ์
1. ์์ธก ๊ฐ๋ฅ์ฑ
๋ฐ์ดํฐ๊ฐ ๋ณํ์ง ์์ผ๋ ์ฝ๋์ ๋์์ ์์ธกํ๊ธฐ ์ฌ์์ ธ. "์ด ๋ณ์๊ฐ ์ด๋์ ๋ฐ๋์์ง?"๋ผ๋ ๊ณ ๋ฏผ์ ํ ํ์๊ฐ ์์ด!
2. ์ค๋ ๋ ์์ ์ฑ
์ฌ๋ฌ ์ค๋ ๋๊ฐ ๋์์ ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ด๋ ์์ ํด. ๋ฐ์ดํฐ ๊ฒฝ์(data race)์ด ๋ฐ์ํ ์ ์๊ฑฐ๋ .
3. ๋๋ฒ๊น
์ฉ์ด์ฑ
๋ฐ์ดํฐ์ ๋ณ๊ฒฝ ์ด๋ ฅ์ ์ถ์ ํ๊ธฐ ์ฌ์. ๊ฐ ์์ ์ ์ํ๊ฐ ๋ณด์กด๋๋๊น.
4. ์บ์ฑ ์ต์ ํ
๊ฐ์ ์
๋ ฅ์ ๋ํ ๊ฒฐ๊ณผ๋ฅผ ์บ์ฑํ ์ ์์ด. ๋ฐ์ดํฐ๊ฐ ๋ณํ์ง ์์ผ๋ ์บ์ ๋ฌดํจํ๋ฅผ ๊ฑฑ์ ํ ํ์๊ฐ ์์ง.
๐ ์ค์ํํธ์์ ๋ถ๋ณ์ฑ ๊ตฌํํ๊ธฐ
์ค์ํํธ๋ let๊ณผ var ํค์๋๋ก ๋ถ๋ณ์ฑ์ ์ ์ดํ ์ ์์ด. ๊ฐ๋ฅํ ํ let์ ์ฌ์ฉํ๋ ๊ฒ ์ข์!
// ๋ถ๋ณ ๋ณ์ ์ ์ธ
let immutableNumber = 42
// immutableNumber = 43 // ์ปดํ์ผ ์๋ฌ!
let immutableArray = [1, 2, 3, 4, 5]
// immutableArray.append(6) // ์ปดํ์ผ ์๋ฌ!
// ๊ฐ๋ณ ๋ณ์
var mutableNumber = 42
mutableNumber = 43 // OK
var mutableArray = [1, 2, 3, 4, 5]
mutableArray.append(6) // OK
// ๊ตฌ์กฐ์ฒด๋ฅผ ํ์ฉํ ๋ถ๋ณ ๋ฐ์ดํฐ ๋ชจ๋ธ
struct User {
let id: Int
let name: String
let email: String
let age: Int
}
// ๋ถ๋ณ ์ธ์คํด์ค
let user = User(id: 1, name: "๊น์ฒ ์", email: "kim@example.com", age: 25)
// user.name = "์ด์ํฌ" // ์ปดํ์ผ ์๋ฌ!
// ๊ฐ์ ๋ณ๊ฒฝํ๋ ค๋ฉด ์ ์ธ์คํด์ค ์์ฑ
let updatedUser = User(
id: user.id,
name: "์ด์ํฌ",
email: user.email,
age: user.age
)
print(user.name) // "๊น์ฒ ์" - ์๋ณธ ์ ์ง
print(updatedUser.name) // "์ด์ํฌ" - ์๋ก์ด ์ธ์คํด์ค
๐ ๏ธ ๋ถ๋ณ ๋ฐ์ดํฐ ๊ตฌ์กฐ ์ ๋ฐ์ดํธ ํจํด
๋ถ๋ณ ๋ฐ์ดํฐ๋ฅผ ๋ค๋ฃฐ ๋๋ ๊ธฐ์กด ๋ฐ์ดํฐ๋ฅผ ์์ ํ๋ ๋์ ์๋ก์ด ๋ณต์ฌ๋ณธ์ ๋ง๋ค์ด ๋ฐํํ๋ ํจํด์ ์ฌ์ฉํด.
// 1. ๋จ์ ๋ณต์ฌ ํจํด
struct Point {
let x: Double
let y: Double
func moved(dx: Double, dy: Double) -> Point {
return Point(x: x + dx, y: y + dy)
}
}
let point1 = Point(x: 10, y: 20)
let point2 = point1.moved(dx: 5, dy: 3)
print(point1) // Point(x: 10, y: 20) - ์๋ณธ ์ ์ง
print(point2) // Point(x: 15, y: 23) - ์๋ก์ด ์ธ์คํด์ค
// 2. ๋น๋ ํจํด ํ์ฉ
struct UserProfile {
let name: String
let age: Int
let email: String
let phoneNumber: String?
let address: String?
func with(name: String? = nil,
age: Int? = nil,
email: String? = nil,
phoneNumber: String? = nil,
address: String? = nil) -> UserProfile {
return UserProfile(
name: name ?? self.name,
age: age ?? self.age,
email: email ?? self.email,
phoneNumber: phoneNumber ?? self.phoneNumber,
address: address ?? self.address
)
}
}
let profile = UserProfile(
name: "๊น์ฒ ์",
age: 30,
email: "kim@example.com",
phoneNumber: nil,
address: nil
)
// ์ผ๋ถ ํ๋๋ง ์
๋ฐ์ดํธ
let updatedProfile = profile.with(age: 31, phoneNumber: "010-1234-5678")
print(profile.age) // 30 - ์๋ณธ ์ ์ง
print(updatedProfile.age) // 31 - ์๋ก์ด ์ธ์คํด์ค
// 3. ์ปฌ๋ ์
์
๋ฐ์ดํธ ํจํด
let numbers = [1, 2, 3, 4, 5]
// ์์ ์ถ๊ฐ (์ ๋ฐฐ์ด ๋ฐํ)
let withSix = numbers + [6]
// ์์ ์ ๊ฑฐ (์ ๋ฐฐ์ด ๋ฐํ)
let withoutFirst = Array(numbers.dropFirst())
// ์์ ๋ณ๊ฒฝ (์ ๋ฐฐ์ด ๋ฐํ)
let doubled = numbers.map { $0 * 2 }
print(numbers) // [1, 2, 3, 4, 5] - ์๋ณธ ์ ์ง
print(withSix) // [1, 2, 3, 4, 5, 6]
print(withoutFirst) // [2, 3, 4, 5]
print(doubled) // [2, 4, 6, 8, 10]
โ ๏ธ ์ฃผ์์ฌํญ: ์ฐธ์กฐ ํ์ ๊ณผ ๋ถ๋ณ์ฑ
ํด๋์ค(์ฐธ์กฐ ํ์
)๋ let์ผ๋ก ์ ์ธํด๋ ๋ด๋ถ ํ๋กํผํฐ๊ฐ var์ด๋ฉด ๋ณ๊ฒฝ ๊ฐ๋ฅํด. ์ง์ ํ ๋ถ๋ณ์ฑ์ ์ํ๋ค๋ฉด ๊ตฌ์กฐ์ฒด(๊ฐ ํ์
)๋ฅผ ์ฌ์ฉํ๊ฑฐ๋, ํด๋์ค์ ๋ชจ๋ ํ๋กํผํฐ๋ฅผ let์ผ๋ก ์ ์ธํด์ผ ํด!
// ํด๋์ค - ์ฐธ์กฐ ํ์
class MutableUser {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
let user1 = MutableUser(name: "๊น์ฒ ์", age: 25)
user1.name = "์ด์ํฌ" // let์ธ๋ฐ๋ ๋ณ๊ฒฝ ๊ฐ๋ฅ! (์ฐธ์กฐ๋ ๋ถ๋ณ, ๋ด์ฉ์ ๊ฐ๋ณ)
// ๊ตฌ์กฐ์ฒด - ๊ฐ ํ์
struct ImmutableUser {
let name: String
let age: Int
}
let user2 = ImmutableUser(name: "๊น์ฒ ์", age: 25)
// user2.name = "์ด์ํฌ" // ์ปดํ์ผ ์๋ฌ! ์ง์ ํ ๋ถ๋ณ์ฑ
์ค์ํํธ์์๋ ๊ฐ๋ฅํ ํ ๊ตฌ์กฐ์ฒด์ let์ ํ์ฉํด์ ๋ถ๋ณ์ฑ์ ์ ์งํ๋ ๊ฒ ์ข์. ์ด๋ ๊ฒ ํ๋ฉด ์ฝ๋๊ฐ ํจ์ฌ ์์ ํ๊ณ ์์ธก ๊ฐ๋ฅํด์ ธ! ๐
๐จ ์ค์ํํธ์ ํจ์ํ ๋๊ตฌ๋ค
์ค์ํํธ๋ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ์ํ ๊ฐ๋ ฅํ ๋๊ตฌ๋ค์ ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณตํด. ์ด ๋๊ตฌ๋ค์ ์ ํ์ฉํ๋ฉด ์ฝ๋๊ฐ ์ ๋ง ๊ฐ๊ฒฐํ๊ณ ์ฝ๊ธฐ ์ฌ์์ ธ! โจ
1๏ธโฃ map - ๋ณํ์ ๋ง๋ฒ์ฌ
map์ ์ปฌ๋ ์
์ ๊ฐ ์์๋ฅผ ๋ณํํด์ ์๋ก์ด ์ปฌ๋ ์
์ ๋ง๋ค์ด์ค. ์๋ณธ์ ์ ํ ๊ฑด๋๋ฆฌ์ง ์๊ณ ๋ง์ด์ผ!
// ๊ธฐ๋ณธ map ์ฌ์ฉ
let numbers = [1, 2, 3, 4, 5]
let squared = numbers.map { $0 * $0 }
print(squared) // [1, 4, 9, 16, 25]
// ํ์
๋ณํ
let strings = numbers.map { String($0) }
print(strings) // ["1", "2", "3", "4", "5"]
// ๋ณต์กํ ๊ฐ์ฒด ๋ณํ
struct Product {
let name: String
let price: Double
}
let products = [
Product(name: "์ฌ๊ณผ", price: 1000),
Product(name: "๋ฐ๋๋", price: 1500),
Product(name: "์ค๋ ์ง", price: 2000)
]
// ๊ฐ๊ฒฉ๋ง ์ถ์ถ
let prices = products.map { $0.price }
print(prices) // [1000.0, 1500.0, 2000.0]
// ํ ์ธ๊ฐ ๊ณ์ฐ
let discountedPrices = products.map { $0.price * 0.9 }
print(discountedPrices) // [900.0, 1350.0, 1800.0]
// ์๋ก์ด ๊ตฌ์กฐ์ฒด ์์ฑ
struct DiscountedProduct {
let name: String
let originalPrice: Double
let discountedPrice: Double
}
let discountedProducts = products.map { product in
DiscountedProduct(
name: product.name,
originalPrice: product.price,
discountedPrice: product.price * 0.9
)
}
// ๋ช
๋ นํ ์คํ์ผ๊ณผ ๋น๊ต
var squaredImperative: [Int] = []
for number in numbers {
squaredImperative.append(number * number)
}
// vs
let squaredFunctional = numbers.map { $0 * $0 }
// ํจ์ํ์ด ํจ์ฌ ๊ฐ๊ฒฐํ๊ณ ์๋๊ฐ ๋ช
ํํด!
2๏ธโฃ filter - ์ ๋ณ์ ๋ฌ์ธ
filter๋ ์กฐ๊ฑด์ ๋ง๋ ์์๋ง ๊ณจ๋ผ๋ด์ ์๋ก์ด ์ปฌ๋ ์
์ ๋ง๋ค์ด์ค.
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// ์ง์๋ง ํํฐ๋ง
let evenNumbers = numbers.filter { $0 % 2 == 0 }
print(evenNumbers) // [2, 4, 6, 8, 10]
// 5๋ณด๋ค ํฐ ์
let greaterThanFive = numbers.filter { $0 > 5 }
print(greaterThanFive) // [6, 7, 8, 9, 10]
// ๋ณต์กํ ์กฐ๊ฑด
struct User {
let name: String
let age: Int
let isActive: Bool
}
let users = [
User(name: "๊น์ฒ ์", age: 25, isActive: true),
User(name: "์ด์ํฌ", age: 17, isActive: true),
User(name: "๋ฐ๋ฏผ์", age: 30, isActive: false),
User(name: "์ ์์ง", age: 22, isActive: true)
]
// ์ฑ์ธ์ด๋ฉด์ ํ์ฑ ์ฌ์ฉ์๋ง
let activeAdults = users.filter { $0.age >= 18 && $0.isActive }
print(activeAdults.map { $0.name }) // ["๊น์ฒ ์", "์ ์์ง"]
// ๋ช
๋ นํ ์คํ์ผ๊ณผ ๋น๊ต
var evenNumbersImperative: [Int] = []
for number in numbers {
if number % 2 == 0 {
evenNumbersImperative.append(number)
}
}
// vs
let evenNumbersFunctional = numbers.filter { $0 % 2 == 0 }
// ์ญ์ ํจ์ํ์ด ๋ ๊ฐ๊ฒฐ!
3๏ธโฃ reduce - ์ง๊ณ์ ์ ๋ฌธ๊ฐ
reduce๋ ์ปฌ๋ ์
์ ๋ชจ๋ ์์๋ฅผ ํ๋์ ๊ฐ์ผ๋ก ํฉ์ณ์ค. ํฉ๊ณ, ๊ณฑ์
, ๋ฌธ์์ด ๊ฒฐํฉ ๋ฑ ๋ค์ํ๊ฒ ํ์ฉํ ์ ์์ด!
let numbers = [1, 2, 3, 4, 5]
// ํฉ๊ณ ๊ณ์ฐ
let sum = numbers.reduce(0) { $0 + $1 }
print(sum) // 15
// ๋ ๊ฐ๋จํ๊ฒ
let sum2 = numbers.reduce(0, +)
print(sum2) // 15
// ๊ณฑ์
let product = numbers.reduce(1, *)
print(product) // 120
// ๋ฌธ์์ด ๊ฒฐํฉ
let words = ["Hello", "Swift", "World"]
let sentence = words.reduce("") { $0 + " " + $1 }
print(sentence) // " Hello Swift World"
// ๋ ๋์ ๋ฐฉ๋ฒ
let sentence2 = words.reduce("") { result, word in
result.isEmpty ? word : result + " " + word
}
print(sentence2) // "Hello Swift World"
// ๋ณต์กํ ์ง๊ณ
struct Transaction {
let amount: Double
let type: String // "income" or "expense"
}
let transactions = [
Transaction(amount: 1000, type: "income"),
Transaction(amount: 500, type: "expense"),
Transaction(amount: 2000, type: "income"),
Transaction(amount: 300, type: "expense")
]
// ์์์ต ๊ณ์ฐ
let netIncome = transactions.reduce(0.0) { result, transaction in
transaction.type == "income" ? result + transaction.amount : result - transaction.amount
}
print(netIncome) // 2200.0
// ๋์
๋๋ฆฌ๋ก ๊ทธ๋ฃนํ
let grouped = transactions.reduce(into: [String: Double]()) { result, transaction in
result[transaction.type, default: 0] += transaction.amount
}
print(grouped) // ["income": 3000.0, "expense": 800.0]
4๏ธโฃ compactMap - nil ์ ๊ฑฐ์ ๊ณ ์
compactMap์ map๊ณผ ๋น์ทํ์ง๋ง, nil ๊ฐ์ ์๋์ผ๋ก ์ ๊ฑฐํด์ค. ์ต์
๋์ ๋ค๋ฃฐ ๋ ์ ๋ง ์ ์ฉํด!
let strings = ["1", "2", "three", "4", "five", "6"]
// ๋ฌธ์์ด์ Int๋ก ๋ณํ (์คํจํ๋ฉด nil)
let numbers = strings.compactMap { Int($0) }
print(numbers) // [1, 2, 4, 6] - nil์ด ์๋์ผ๋ก ์ ๊ฑฐ๋จ!
// map์ ์ฌ์ฉํ๋ฉด?
let numbersWithNil = strings.map { Int($0) }
print(numbersWithNil) // [Optional(1), Optional(2), nil, Optional(4), nil, Optional(6)]
// ์ต์
๋ ์ฒด์ด๋๊ณผ ํจ๊ป
struct Person {
let name: String
let email: String?
}
let people = [
Person(name: "๊น์ฒ ์", email: "kim@example.com"),
Person(name: "์ด์ํฌ", email: nil),
Person(name: "๋ฐ๋ฏผ์", email: "park@example.com"),
Person(name: "์ ์์ง", email: nil)
]
// ์ด๋ฉ์ผ์ด ์๋ ์ฌ๋๋ง
let emails = people.compactMap { $0.email }
print(emails) // ["kim@example.com", "park@example.com"]
// ๋ณต์กํ ๋ณํ
let validEmails = people.compactMap { person -> String? in
guard let email = person.email,
email.contains("@") else {
return nil
}
return email.lowercased()
}
print(validEmails) // ["kim@example.com", "park@example.com"]
5๏ธโฃ flatMap - ์ค์ฒฉ ์ ๊ฑฐ์ ๋ฌ์ธ
flatMap์ ์ค์ฒฉ๋ ์ปฌ๋ ์
์ ํํํ๊ฒ ๋ง๋ค์ด์ค. 2์ฐจ์ ๋ฐฐ์ด์ 1์ฐจ์์ผ๋ก ๋ฐ๊ฟ ๋ ์ ์ฉํด!
// 2์ฐจ์ ๋ฐฐ์ด ํํํ
let nestedNumbers = [[1, 2, 3], [4, 5], [6, 7, 8, 9]]
let flattened = nestedNumbers.flatMap { $0 }
print(flattened) // [1, 2, 3, 4, 5, 6, 7, 8, 9]
// ๋ฌธ์์ด ๋ฐฐ์ด์ ๋ฌธ์ ๋ฐฐ์ด๋ก
let words = ["Hello", "Swift"]
let characters = words.flatMap { $0 }
print(characters) // ["H", "e", "l", "l", "o", "S", "w", "i", "f", "t"]
// ๋ณต์กํ ์์
struct Department {
let name: String
let employees: [String]
}
let departments = [
Department(name: "๊ฐ๋ฐํ", employees: ["๊น์ฒ ์", "์ด์ํฌ"]),
Department(name: "๋์์ธํ", employees: ["๋ฐ๋ฏผ์"]),
Department(name: "๋ง์ผํ
ํ", employees: ["์ ์์ง", "์ต์์", "ํ์ง๋ฏผ"])
]
// ๋ชจ๋ ์ง์ ์ด๋ฆ ์ถ์ถ
let allEmployees = departments.flatMap { $0.employees }
print(allEmployees) // ["๊น์ฒ ์", "์ด์ํฌ", "๋ฐ๋ฏผ์", "์ ์์ง", "์ต์์", "ํ์ง๋ฏผ"]
// ์ต์
๋๊ณผ ํจ๊ป ์ฌ์ฉ (Swift 4.1 ์ดํ compactMap ๊ถ์ฅ)
let optionalNumbers: [Int?] = [1, 2, nil, 4, nil, 6]
let validNumbers = optionalNumbers.compactMap { $0 }
print(validNumbers) // [1, 2, 4, 6]
๐ ํจ์ ์ฒด์ด๋์ ๋ง๋ฒ
์ด ํจ์๋ค์ ์ฐ๊ฒฐํด์ ์ฌ์ฉํ๋ฉด ์ ๋ง ๊ฐ๋ ฅํด์ ธ! ๋ณต์กํ ๋ฐ์ดํฐ ์ฒ๋ฆฌ๋ฅผ ํ ์ค๋ก ํํํ ์ ์์ด.
struct Product {
let name: String
let price: Double
let category: String
let inStock: Bool
}
let products = [
Product(name: "๋งฅ๋ถ", price: 2000000, category: "์ ์์ ํ", inStock: true),
Product(name: "์์ดํจ๋", price: 800000, category: "์ ์์ ํ", inStock: false),
Product(name: "์ฑ
์", price: 150000, category: "๊ฐ๊ตฌ", inStock: true),
Product(name: "์์", price: 200000, category: "๊ฐ๊ตฌ", inStock: true),
Product(name: "์์ดํฐ", price: 1200000, category: "์ ์์ ํ", inStock: true)
]
// ์ฌ๊ณ ๊ฐ ์๋ ์ ์์ ํ์ ์ด ๊ฐ๊ฒฉ
let totalElectronicsPrice = products
.filter { $0.category == "์ ์์ ํ" && $0.inStock } // ์ ์์ ํ + ์ฌ๊ณ ์์
.map { $0.price } // ๊ฐ๊ฒฉ๋ง ์ถ์ถ
.reduce(0, +) // ํฉ๊ณ
print(totalElectronicsPrice) // 3200000
// ์นดํ
๊ณ ๋ฆฌ๋ณ ํ๊ท ๊ฐ๊ฒฉ
let averagePriceByCategory = Dictionary(grouping: products, by: { $0.category })
.mapValues { products in
products.map { $0.price }.reduce(0, +) / Double(products.count)
}
print(averagePriceByCategory)
// ["์ ์์ ํ": 1333333.33, "๊ฐ๊ตฌ": 175000.0]
// ์ฌ๊ณ ์๋ ์ํ ์ด๋ฆ๋ค์ ์ผํ๋ก ์ฐ๊ฒฐ
let availableProducts = products
.filter { $0.inStock }
.map { $0.name }
.joined(separator: ", ")
print(availableProducts) // "๋งฅ๋ถ, ์ฑ
์, ์์, ์์ดํฐ"
์ด๋ ๊ฒ ํจ์๋ค์ ์ฒด์ด๋ํ๋ฉด ์ฝ๋๊ฐ ์ ๋ง ์ฝ๊ธฐ ์ฌ์์ ธ. ๋ง์น ์์ด ๋ฌธ์ฅ์ฒ๋ผ "ํํฐ๋งํ๊ณ , ๋ณํํ๊ณ , ํฉ์ณ๋ผ"๋ ์์ผ๋ก ์ฝํ๊ฑฐ๋ ! ๐
๐ฏ ๊ณ ์ฐจ ํจ์(Higher-Order Functions)
๊ณ ์ฐจ ํจ์๋ ํจ์๋ฅผ ์ธ์๋ก ๋ฐ๊ฑฐ๋ ํจ์๋ฅผ ๋ฐํํ๋ ํจ์์ผ. ์ค์ํํธ์์ ํจ์๋ ์ผ๊ธ ๊ฐ์ฒด(First-Class Citizen)์ด๊ธฐ ๋๋ฌธ์ ์ด๊ฒ ๊ฐ๋ฅํด!
๐ง ํจ์๋ฅผ ์ธ์๋ก ๋ฐ๊ธฐ
// ๊ธฐ๋ณธ ์์
func applyOperation(_ a: Int, _ b: Int, operation: (Int, Int) -> Int) -> Int {
return operation(a, b)
}
// ์ฌ์ฉ
let sum = applyOperation(5, 3, operation: { $0 + $1 })
print(sum) // 8
let product = applyOperation(5, 3, operation: { $0 * $1 })
print(product) // 15
// ๋ ๊ฐ๋จํ๊ฒ
let sum2 = applyOperation(5, 3, operation: +)
let product2 = applyOperation(5, 3, operation: *)
// ์ค์ฉ์ ์ธ ์์
func processArray(_ array: [Int], with processor: (Int) -> Int) -> [Int] {
return array.map(processor)
}
let numbers = [1, 2, 3, 4, 5]
let doubled = processArray(numbers) { $0 * 2 }
let squared = processArray(numbers) { $0 * $0 }
print(doubled) // [2, 4, 6, 8, 10]
print(squared) // [1, 4, 9, 16, 25]
// ์กฐ๊ฑด๋ถ ์ฒ๋ฆฌ
func filterAndTransform<t, u="">(_ array: [T],
filter: (T) -> Bool,
transform: (T) -> U) -> [U] {
return array.filter(filter).map(transform)
}
let evenSquares = filterAndTransform(numbers,
filter: { $0 % 2 == 0 },
transform: { $0 * $0 })
print(evenSquares) // [4, 16]</t,>
๐ ํจ์๋ฅผ ๋ฐํํ๊ธฐ
// ๊ธฐ๋ณธ ์์
func makeMultiplier(factor: Int) -> (Int) -> Int {
return { number in
return number * factor
}
}
let double = makeMultiplier(factor: 2)
let triple = makeMultiplier(factor: 3)
print(double(5)) // 10
print(triple(5)) // 15
// ๋ ๋ณต์กํ ์์
func makeValidator(minLength: Int, maxLength: Int) -> (String) -> Bool {
return { text in
return text.count >= minLength && text.count <= maxLength
}
}
let isValidUsername = makeValidator(minLength: 3, maxLength: 20)
let isValidPassword = makeValidator(minLength: 8, maxLength: 50)
print(isValidUsername("kim")) // true
print(isValidUsername("ab")) // false
print(isValidPassword("12345")) // false
print(isValidPassword("12345678")) // true
// ํด๋ก์ ์บก์ฒ๋ฅผ ํ์ฉํ ์นด์ดํฐ
func makeCounter() -> () -> Int {
var count = 0
return {
count += 1
return count
}
}
let counter1 = makeCounter()
let counter2 = makeCounter()
print(counter1()) // 1
print(counter1()) // 2
print(counter1()) // 3
print(counter2()) // 1 - ๋
๋ฆฝ์ ์ธ ์นด์ดํฐ
print(counter2()) // 2
// ํจ์ ์กฐํฉ
func compose<a, b,="" c="">(_ f: @escaping (B) -> C, _ g: @escaping (A) -> B) -> (A) -> C {
return { x in
f(g(x))
}
}
let addOne: (Int) -> Int = { $0 + 1 }
let multiplyByTwo: (Int) -> Int = { $0 * 2 }
let addOneThenMultiply = compose(multiplyByTwo, addOne)
print(addOneThenMultiply(5)) // (5 + 1) * 2 = 12</a,>
๐จ ์ปค๋ง(Currying)
์ปค๋ง์ ์ฌ๋ฌ ๊ฐ์ ์ธ์๋ฅผ ๋ฐ๋ ํจ์๋ฅผ ํ๋์ ์ธ์๋ฅผ ๋ฐ๋ ํจ์๋ค์ ์ฒด์ธ์ผ๋ก ๋ฐ๊พธ๋ ๊ธฐ๋ฒ์ด์ผ.
// ์ผ๋ฐ ํจ์
func add(_ a: Int, _ b: Int) -> Int {
return a + b
}
// ์ปค๋ง๋ ํจ์
func curriedAdd(_ a: Int) -> (Int) -> Int {
return { b in
return a + b
}
}
// ์ฌ์ฉ
let add5 = curriedAdd(5)
print(add5(3)) // 8
print(add5(10)) // 15
// ์ค์ฉ์ ์ธ ์์
func makeGreeting(_ greeting: String) -> (String) -> String {
return { name in
return "\(greeting), \(name)!"
}
}
let sayHello = makeGreeting("์๋
ํ์ธ์")
let sayGoodbye = makeGreeting("์๋
ํ ๊ฐ์ธ์")
print(sayHello("๊น์ฒ ์")) // "์๋
ํ์ธ์, ๊น์ฒ ์!"
print(sayGoodbye("์ด์ํฌ")) // "์๋
ํ ๊ฐ์ธ์, ์ด์ํฌ!"
// ๋ ๋ณต์กํ ์ปค๋ง
func multiply(_ a: Int) -> (Int) -> (Int) -> Int {
return { b in
return { c in
return a * b * c
}
}
}
let result = multiply(2)(3)(4)
print(result) // 24
// ๋ถ๋ถ ์ ์ฉ
let multiplyBy2 = multiply(2)
let multiplyBy2And3 = multiplyBy2(3)
print(multiplyBy2And3(4)) // 24
๐๏ธ ์ค์ ํจํด: ํจ์ํ ์ํคํ ์ฒ
์ด์ ๋ฐฐ์ด ๋ด์ฉ๋ค์ ์ค์ ํ๋ก์ ํธ์ ์ด๋ป๊ฒ ์ ์ฉํ๋์ง ์์๋ณด์! ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ์์น์ ๋ฐ๋ฅด๋ฉด ์ฝ๋๊ฐ ์ ๋ง ๊น๋ํด์ ธ. ๐จ
๐ฆ ๋ถ๋ณ ๋ฐ์ดํฐ ๋ชจ๋ธ ์ค๊ณ
// ๋์ ์: ๊ฐ๋ณ ์ํ
class MutableShoppingCart {
var items: [Product] = []
var totalPrice: Double = 0
func addItem(_ product: Product) {
items.append(product)
totalPrice += product.price
}
func removeItem(at index: Int) {
let product = items.remove(at: index)
totalPrice -= product.price
}
}
// ์ข์ ์: ๋ถ๋ณ ์ํ
struct Product {
let id: String
let name: String
let price: Double
}
struct ShoppingCart {
let items: [Product]
var totalPrice: Double {
items.reduce(0) { $0 + $1.price }
}
var itemCount: Int {
items.count
}
func adding(_ product: Product) -> ShoppingCart {
return ShoppingCart(items: items + [product])
}
func removing(at index: Int) -> ShoppingCart {
var newItems = items
newItems.remove(at: index)
return ShoppingCart(items: newItems)
}
func removingProduct(withId id: String) -> ShoppingCart {
let newItems = items.filter { $0.id != id }
return ShoppingCart(items: newItems)
}
func applyingDiscount(_ percentage: Double) -> ShoppingCart {
let discountedItems = items.map { product in
Product(id: product.id,
name: product.name,
price: product.price * (1 - percentage / 100))
}
return ShoppingCart(items: discountedItems)
}
}
// ์ฌ์ฉ
let emptyCart = ShoppingCart(items: [])
let product1 = Product(id: "1", name: "๋งฅ๋ถ", price: 2000000)
let product2 = Product(id: "2", name: "์์ดํจ๋", price: 800000)
let cart1 = emptyCart.adding(product1)
let cart2 = cart1.adding(product2)
let discountedCart = cart2.applyingDiscount(10)
print(cart1.totalPrice) // 2000000
print(cart2.totalPrice) // 2800000
print(discountedCart.totalPrice) // 2520000
// ์๋ณธ์ ๋ณํ์ง ์์!
print(emptyCart.itemCount) // 0
print(cart1.itemCount) // 1
print(cart2.itemCount) // 2
๐ ์ํ ๋ณํ ํ์ดํ๋ผ์ธ
// ์ฌ์ฉ์ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ํ์ดํ๋ผ์ธ
struct User {
let id: Int
let name: String
let email: String
let age: Int
let isActive: Bool
}
struct UserViewModel {
let displayName: String
let email: String
let statusText: String
}
// ์์ ํจ์๋ค๋ก ๊ตฌ์ฑ๋ ๋ณํ ํ์ดํ๋ผ์ธ
func filterActiveAdults(_ users: [User]) -> [User] {
return users.filter { $0.isActive && $0.age >= 18 }
}
func sortByName(_ users: [User]) -> [User] {
return users.sorted { $0.name < $1.name }
}
func toViewModels(_ users: [User]) -> [UserViewModel] {
return users.map { user in
UserViewModel(
displayName: user.name,
email: user.email,
statusText: user.isActive ? "ํ์ฑ" : "๋นํ์ฑ"
)
}
}
// ํ์ดํ๋ผ์ธ ์กฐํฉ
func processUsers(_ users: [User]) -> [UserViewModel] {
return toViewModels(sortByName(filterActiveAdults(users)))
}
// ๋ ๋์ ๋ฐฉ๋ฒ: ์ปค์คํ
์ฐ์ฐ์
infix operator |> : AdditionPrecedence
func |> <t, u="">(value: T, function: (T) -> U) -> U {
return function(value)
}
// ์ฌ์ฉ
let users = [
User(id: 1, name: "๊น์ฒ ์", email: "kim@example.com", age: 25, isActive: true),
User(id: 2, name: "์ด์ํฌ", email: "lee@example.com", age: 17, isActive: true),
User(id: 3, name: "๋ฐ๋ฏผ์", email: "park@example.com", age: 30, isActive: false),
User(id: 4, name: "์ ์์ง", email: "jung@example.com", age: 22, isActive: true)
]
let viewModels = users
|> filterActiveAdults
|> sortByName
|> toViewModels
print(viewModels.map { $0.displayName }) // ["๊น์ฒ ์", "์ ์์ง"]</t,>
๐ญ Result ํ์ ์ผ๋ก ์๋ฌ ์ฒ๋ฆฌ
// ์์ ํจ์ํ ์๋ฌ ์ฒ๋ฆฌ
enum ValidationError: Error {
case emptyName
case invalidEmail
case ageTooYoung
case ageTooOld
}
struct UserInput {
let name: String
let email: String
let age: Int
}
// ๊ฐ ๊ฒ์ฆ์ ์์ ํจ์๋ก
func validateName(_ name: String) -> Result<string, validationerror=""> {
guard !name.isEmpty else {
return .failure(.emptyName)
}
return .success(name)
}
func validateEmail(_ email: String) -> Result<string, validationerror=""> {
guard email.contains("@") && email.contains(".") else {
return .failure(.invalidEmail)
}
return .success(email)
}
func validateAge(_ age: Int) -> Result<int, validationerror=""> {
guard age >= 18 else {
return .failure(.ageTooYoung)
}
guard age <= 120 else {
return .failure(.ageTooOld)
}
return .success(age)
}
// ๊ฒ์ฆ ์กฐํฉ
func validateUser(_ input: UserInput) -> Result<user, validationerror=""> {
let nameResult = validateName(input.name)
let emailResult = validateEmail(input.email)
let ageResult = validateAge(input.age)
// ๋ชจ๋ ๊ฒ์ฆ์ด ์ฑ๊ณตํด์ผ User ์์ฑ
switch (nameResult, emailResult, ageResult) {
case (.success(let name), .success(let email), .success(let age)):
return .success(User(id: 0, name: name, email: email, age: age, isActive: true))
case (.failure(let error), _, _),
(_, .failure(let error), _),
(_, _, .failure(let error)):
return .failure(error)
}
}
// ์ฌ์ฉ
let input1 = UserInput(name: "๊น์ฒ ์", email: "kim@example.com", age: 25)
let result1 = validateUser(input1)
switch result1 {
case .success(let user):
print("์ฌ์ฉ์ ์์ฑ ์ฑ๊ณต: \(user.name)")
case .failure(let error):
print("๊ฒ์ฆ ์คํจ: \(error)")
}
let input2 = UserInput(name: "", email: "invalid", age: 15)
let result2 = validateUser(input2)
switch result2 {
case .success(let user):
print("์ฌ์ฉ์ ์์ฑ ์ฑ๊ณต: \(user.name)")
case .failure(let error):
print("๊ฒ์ฆ ์คํจ: \(error)") // "๊ฒ์ฆ ์คํจ: emptyName"
}</user,></int,></string,></string,>
๐ ํจ์ ์กฐํฉ ํจํด
์์ ์์ ํจ์๋ค์ ์กฐํฉํด์ ๋ณต์กํ ๋ก์ง์ ๋ง๋ค๋ฉด, ๊ฐ ๋ถ๋ถ์ ๋ ๋ฆฝ์ ์ผ๋ก ํ ์คํธํ๊ณ ์ฌ์ฌ์ฉํ ์ ์์ด!
๐งช ํ ์คํธํ๊ธฐ ์ฌ์ด ์ฝ๋
ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ๊ฐ์ฅ ํฐ ์ฅ์ ์ค ํ๋๋ ํ ์คํธ๊ฐ ์ ๋ง ์ฝ๋ค๋ ๊ฑฐ์ผ! ์์ ํจ์๋ ์ธ๋ถ ์ํ์ ์์กดํ์ง ์์ผ๋๊น ํ ์คํธ ์ฝ๋ ์์ฑ์ด ๊ฐ๋จํด์ ธ. ๐ฏ
import XCTest
// ํ
์คํธํ ์์ ํจ์๋ค
func calculateDiscount(price: Double, percentage: Double) -> Double {
return price * (1 - percentage / 100)
}
func isValidEmail(_ email: String) -> Bool {
return email.contains("@") && email.contains(".")
}
func filterAdults(_ users: [User]) -> [User] {
return users.filter { $0.age >= 18 }
}
// ํ
์คํธ ์ฝ๋
class FunctionalProgrammingTests: XCTestCase {
// ์์ ํจ์๋ ํ
์คํธ๊ฐ ์ ๋ง ์ฌ์!
func testCalculateDiscount() {
// Given
let price = 10000.0
let percentage = 10.0
// When
let result = calculateDiscount(price: price, percentage: percentage)
// Then
XCTAssertEqual(result, 9000.0)
}
func testIsValidEmail() {
// ์ฌ๋ฌ ์ผ์ด์ค๋ฅผ ๊ฐ๋จํ๊ฒ ํ
์คํธ
XCTAssertTrue(isValidEmail("test@example.com"))
XCTAssertFalse(isValidEmail("invalid"))
XCTAssertFalse(isValidEmail("@example.com"))
XCTAssertFalse(isValidEmail("test@"))
}
func testFilterAdults() {
// Given
let users = [
User(id: 1, name: "๊น์ฒ ์", email: "kim@example.com", age: 25, isActive: true),
User(id: 2, name: "์ด์ํฌ", email: "lee@example.com", age: 17, isActive: true),
User(id: 3, name: "๋ฐ๋ฏผ์", email: "park@example.com", age: 30, isActive: true)
]
// When
let adults = filterAdults(users)
// Then
XCTAssertEqual(adults.count, 2)
XCTAssertTrue(adults.allSatisfy { $0.age >= 18 })
}
// ๋ถ๋ณ ๋ฐ์ดํฐ ๊ตฌ์กฐ ํ
์คํธ
func testShoppingCartImmutability() {
// Given
let product = Product(id: "1", name: "๋งฅ๋ถ", price: 2000000)
let emptyCart = ShoppingCart(items: [])
// When
let cart1 = emptyCart.adding(product)
let cart2 = cart1.adding(product)
// Then
XCTAssertEqual(emptyCart.itemCount, 0) // ์๋ณธ ์ ์ง
XCTAssertEqual(cart1.itemCount, 1)
XCTAssertEqual(cart2.itemCount, 2)
XCTAssertEqual(cart2.totalPrice, 4000000)
}
}
// ํ๋กํผํฐ ๊ธฐ๋ฐ ํ
์คํธ (Property-Based Testing)
func testMapPreservesLength() {
let numbers = [1, 2, 3, 4, 5]
let doubled = numbers.map { $0 * 2 }
// map์ ๊ธธ์ด๋ฅผ ๋ณด์กดํด์ผ ํจ
XCTAssertEqual(numbers.count, doubled.count)
}
func testFilterReducesOrMaintainsLength() {
let numbers = [1, 2, 3, 4, 5]
let evens = numbers.filter { $0 % 2 == 0 }
// filter๋ ๊ธธ์ด๋ฅผ ์ค์ด๊ฑฐ๋ ์ ์งํด์ผ ํจ
XCTAssertLessThanOrEqual(evens.count, numbers.count)
}
๐ฏ ํ ์คํธ ๊ฐ๋ฅํ ์ฝ๋์ ํน์ง
โข ์์ธก ๊ฐ๋ฅ: ๊ฐ์ ์
๋ ฅ์ ํญ์ ๊ฐ์ ์ถ๋ ฅ
โข ๋
๋ฆฝ์ : ์ธ๋ถ ์ํ์ ์์กดํ์ง ์์
โข ๋ถ์์ฉ ์์: ์ ์ญ ์ํ๋ฅผ ๋ณ๊ฒฝํ์ง ์์
โข ์์ ๋จ์: ๊ฐ ํจ์๊ฐ ํ๋์ ์ฑ
์๋ง ๊ฐ์ง
โก ์ฑ๋ฅ ์ต์ ํ ํ
ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ด ๋นํจ์จ์ ์ด๋ผ๋ ์คํด๊ฐ ์๋๋ฐ, ์ฌ์ค ์ ๋๋ก ์ฌ์ฉํ๋ฉด ์ฑ๋ฅ๋ ์ข์! ๋ช ๊ฐ์ง ์ต์ ํ ํ์ ์์๋ณด์. ๐
1๏ธโฃ Lazy ํ๊ฐ ํ์ฉ
// ์ผ๋ฐ ๋ฐฐ์ด ์ฐ์ฐ - ์ฆ์ ํ๊ฐ
let numbers = Array(1...1000000)
let result1 = numbers
.map { $0 * 2 } // ๋ฐฑ๋ง ๊ฐ ์ฒ๋ฆฌ
.filter { $0 > 100 } // ๋ฐฑ๋ง ๊ฐ ์ฒ๋ฆฌ
.prefix(10) // 10๊ฐ๋ง ํ์ํ๋ฐ...
// Lazy ํ๊ฐ - ํ์ํ ๋งํผ๋ง ์ฒ๋ฆฌ
let result2 = numbers.lazy
.map { $0 * 2 } // ํ์ํ ๋งํผ๋ง ์ฒ๋ฆฌ
.filter { $0 > 100 } // ํ์ํ ๋งํผ๋ง ์ฒ๋ฆฌ
.prefix(10) // 10๊ฐ๋ง ์ฒ๋ฆฌํ๊ณ ์ค๋จ!
// ์ฑ๋ฅ ๋น๊ต
import Foundation
func measureTime(_ closure: () -> Void) {
let start = Date()
closure()
let end = Date()
print("์คํ ์๊ฐ: \(end.timeIntervalSince(start))์ด")
}
measureTime {
let _ = numbers
.map { $0 * 2 }
.filter { $0 > 100 }
.prefix(10)
}
// ์คํ ์๊ฐ: ์ฝ 0.5์ด
measureTime {
let _ = Array(numbers.lazy
.map { $0 * 2 }
.filter { $0 > 100 }
.prefix(10))
}
// ์คํ ์๊ฐ: ์ฝ 0.001์ด - 500๋ฐฐ ๋น ๋ฆ!
// Lazy์ ์ฃผ์์ฌํญ
let lazyResult = numbers.lazy.map { $0 * 2 }
// ์์ง ๊ณ์ฐ๋์ง ์์!
let array = Array(lazyResult)
// ์ด์ ๊ณ์ฐ๋จ!
2๏ธโฃ ๋ถํ์ํ ๋ณต์ฌ ํผํ๊ธฐ
// ๋นํจ์จ์ - ๋งค๋ฒ ์ ๋ฐฐ์ด ์์ฑ
func inefficientAppend(_ array: [Int], _ element: Int) -> [Int] {
var newArray = array // ๋ณต์ฌ!
newArray.append(element)
return newArray
}
// ๋ ๋์ ๋ฐฉ๋ฒ - ๋ฐฐ์ด ์ฐ๊ฒฐ ์ฐ์ฐ์
func efficientAppend(_ array: [Int], _ element: Int) -> [Int] {
return array + [element] // ์ต์ ํ๋จ
}
// ์ฌ๋ฌ ์์ ์ถ๊ฐํ ๋
let numbers = [1, 2, 3]
// ๋นํจ์จ์
var result1 = numbers
result1 = efficientAppend(result1, 4)
result1 = efficientAppend(result1, 5)
result1 = efficientAppend(result1, 6)
// ํจ์จ์
let result2 = numbers + [4, 5, 6]
// reduce(into:) ์ฌ์ฉ - ๊ฐ๋ณ ๋์ฐ๊ธฐ
let sum = numbers.reduce(into: 0) { result, number in
result += number // ๋ณต์ฌ ์์ด ๋์ฐ๊ธฐ ๋ณ๊ฒฝ
}
// ๋์
๋๋ฆฌ ๊ทธ๋ฃนํ ์ต์ ํ
struct Product {
let category: String
let name: String
}
let products = [
Product(category: "์ ์์ ํ", name: "๋งฅ๋ถ"),
Product(category: "์ ์์ ํ", name: "์์ดํจ๋"),
Product(category: "๊ฐ๊ตฌ", name: "์ฑ
์")
]
// ๋นํจ์จ์
let grouped1 = products.reduce([String: [Product]]()) { result, product in
var newResult = result // ๋งค๋ฒ ๋ณต์ฌ!
newResult[product.category, default: []].append(product)
return newResult
}
// ํจ์จ์ - reduce(into:)
let grouped2 = products.reduce(into: [String: [Product]]()) { result, product in
result[product.category, default: []].append(product) // ๋ณต์ฌ ์์!
}
3๏ธโฃ ๋ฉ๋ชจ์ด์ ์ด์ (Memoization)
// ๋นํจ์จ์ ์ธ ์ฌ๊ท ํจ์
func fibonacci(_ n: Int) -> Int {
if n <= 1 { return n }
return fibonacci(n - 1) + fibonacci(n - 2)
}
// fibonacci(40) ํธ์ถ ์ ์ฝ 2์ด ์์...
// ๋ฉ๋ชจ์ด์ ์ด์
์ ์ฉ
class Memoizer<input: hashable,="" output=""> {
private var cache: [Input: Output] = [:]
private let function: (Input) -> Output
init(_ function: @escaping (Input) -> Output) {
self.function = function
}
func callAsFunction(_ input: Input) -> Output {
if let cached = cache[input] {
return cached
}
let result = function(input)
cache[input] = result
return result
}
}
// ์ฌ์ฉ
let memoizedFib = Memoizer<int, int=""> { n in
if n <= 1 { return n }
return memoizedFib(n - 1) + memoizedFib(n - 2)
}
// fibonacci(40) ํธ์ถ ์ ์ฝ 0.001์ด - 2000๋ฐฐ ๋น ๋ฆ!
// ์ค์ฉ์ ์ธ ์์
struct ExpensiveCalculation {
static let memoized = Memoizer<string, string=""> { input in
// ๋ณต์กํ ๊ณ์ฐ...
Thread.sleep(forTimeInterval: 1) // 1์ด ๊ฑธ๋ฆฌ๋ ์์
return input.uppercased()
}
}
// ์ฒซ ํธ์ถ: 1์ด
let result1 = ExpensiveCalculation.memoized("hello")
// ๋ ๋ฒ์งธ ํธ์ถ: ์ฆ์ ๋ฐํ!
let result2 = ExpensiveCalculation.memoized("hello")</string,></int,></input:>
โ ๏ธ ์ต์ ํ ์ฃผ์์ฌํญ
โข ์ฑ๋ฅ ๋ฌธ์ ๊ฐ ์ค์ ๋ก ์์ ๋๋ง ์ต์ ํํด
โข ํ๋กํ์ผ๋ง์ผ๋ก ๋ณ๋ชฉ ์ง์ ์ ๋จผ์ ์ฐพ์
โข ๊ฐ๋
์ฑ์ ํฌ์ํ๋ฉด์๊น์ง ์ต์ ํํ์ง ๋ง
โข ๋ฉ๋ชจ์ด์ ์ด์
์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ์ฃผ์ํด
๐ ์ค์ ํ๋ก์ ํธ ์์
์ด์ ๋ฐฐ์ด ๋ด์ฉ์ ์ข ํฉํด์ ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํด๋ณด์! ๊ฐ๋จํ ํ ์ผ ๊ด๋ฆฌ ์ฑ์ ํจ์ํ ์คํ์ผ๋ก ๋ง๋ค์ด๋ณผ๊ฒ. ๐ฑ
// 1. ๋ถ๋ณ ๋ฐ์ดํฐ ๋ชจ๋ธ
struct Todo {
let id: UUID
let title: String
let description: String
let isCompleted: Bool
let createdAt: Date
let priority: Priority
enum Priority: Int, Comparable {
case low = 0
case medium = 1
case high = 2
static func < (lhs: Priority, rhs: Priority) -> Bool {
return lhs.rawValue < rhs.rawValue
}
}
}
// 2. ์ํ ๊ด๋ฆฌ
struct TodoState {
let todos: [Todo]
let filter: TodoFilter
enum TodoFilter {
case all
case active
case completed
}
// ํํฐ๋ง๋ ํ ์ผ ๋ชฉ๋ก
var filteredTodos: [Todo] {
switch filter {
case .all:
return todos
case .active:
return todos.filter { !$0.isCompleted }
case .completed:
return todos.filter { $0.isCompleted }
}
}
// ํต๊ณ
var statistics: Statistics {
Statistics(
total: todos.count,
completed: todos.filter { $0.isCompleted }.count,
active: todos.filter { !$0.isCompleted }.count
)
}
struct Statistics {
let total: Int
let completed: Int
let active: Int
var completionRate: Double {
guard total > 0 else { return 0 }
return Double(completed) / Double(total) * 100
}
}
}
// 3. ์์ ํจ์๋ก ์ํ ๋ณ๊ฒฝ
extension TodoState {
func adding(_ todo: Todo) -> TodoState {
return TodoState(todos: todos + [todo], filter: filter)
}
func removing(id: UUID) -> TodoState {
let newTodos = todos.filter { $0.id != id }
return TodoState(todos: newTodos, filter: filter)
}
func toggling(id: UUID) -> TodoState {
let newTodos = todos.map { todo in
guard todo.id == id else { return todo }
return Todo(
id: todo.id,
title: todo.title,
description: todo.description,
isCompleted: !todo.isCompleted,
createdAt: todo.createdAt,
priority: todo.priority
)
}
return TodoState(todos: newTodos, filter: filter)
}
func updating(id: UUID, title: String, description: String, priority: Todo.Priority) -> TodoState {
let newTodos = todos.map { todo in
guard todo.id == id else { return todo }
return Todo(
id: todo.id,
title: title,
description: description,
isCompleted: todo.isCompleted,
createdAt: todo.createdAt,
priority: priority
)
}
return TodoState(todos: newTodos, filter: filter)
}
func changingFilter(to newFilter: TodoFilter) -> TodoState {
return TodoState(todos: todos, filter: newFilter)
}
func sortedByPriority() -> TodoState {
let sortedTodos = todos.sorted { $0.priority > $1.priority }
return TodoState(todos: sortedTodos, filter: filter)
}
func sortedByDate() -> TodoState {
let sortedTodos = todos.sorted { $0.createdAt > $1.createdAt }
return TodoState(todos: sortedTodos, filter: filter)
}
}
// 4. ์ฌ์ฉ ์์
var state = TodoState(todos: [], filter: .all)
// ํ ์ผ ์ถ๊ฐ
let todo1 = Todo(
id: UUID(),
title: "์ค์ํํธ ๊ณต๋ถํ๊ธฐ",
description: "ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ๋ง์คํฐ",
isCompleted: false,
createdAt: Date(),
priority: .high
)
state = state.adding(todo1)
let todo2 = Todo(
id: UUID(),
title: "์ฅ๋ณด๊ธฐ",
description: "์ฐ์ , ๋นต, ๊ณ๋",
isCompleted: false,
createdAt: Date(),
priority: .medium
)
state = state.adding(todo2)
// ํ ์ผ ์๋ฃ
state = state.toggling(id: todo1.id)
// ํํฐ ๋ณ๊ฒฝ
state = state.changingFilter(to: .active)
// ํต๊ณ ํ์ธ
let stats = state.statistics
print("์ ์ฒด: \(stats.total), ์๋ฃ: \(stats.completed), ์งํ์ค: \(stats.active)")
print("์๋ฃ์จ: \(stats.completionRate)%")
// ์ฐ์ ์์๋ก ์ ๋ ฌ
state = state.sortedByPriority()
// 5. ๊ณ ๊ธ ๊ธฐ๋ฅ - ๊ฒ์
extension TodoState {
func searching(query: String) -> TodoState {
guard !query.isEmpty else { return self }
let searchResults = todos.filter { todo in
todo.title.localizedCaseInsensitiveContains(query) ||
todo.description.localizedCaseInsensitiveContains(query)
}
return TodoState(todos: searchResults, filter: filter)
}
}
// 6. ๊ณ ๊ธ ๊ธฐ๋ฅ - ์ผ๊ด ์์
extension TodoState {
func completingAll() -> TodoState {
let completedTodos = todos.map { todo in
Todo(
id: todo.id,
title: todo.title,
description: todo.description,
isCompleted: true,
createdAt: todo.createdAt,
priority: todo.priority
)
}
return TodoState(todos: completedTodos, filter: filter)
}
func removingCompleted() -> TodoState {
let activeTodos = todos.filter { !$0.isCompleted }
return TodoState(todos: activeTodos, filter: filter)
}
}
์ด๋ ๊ฒ ํจ์ํ ์คํ์ผ๋ก ์์ฑํ๋ฉด ์ํ ๋ณ๊ฒฝ์ด ๋ช ํํ๊ณ ์์ธก ๊ฐ๋ฅํด์ ธ. ๊ฐ ํจ์๊ฐ ์๋ก์ด ์ํ๋ฅผ ๋ฐํํ๋๊น ์ด์ ์ํ๋ก ๋๋๋ฆฌ๊ธฐ(undo)๋ ์ฝ๊ณ , ํ ์คํธํ๊ธฐ๋ ํธํด! ๐
๐ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ vs ๊ฐ์ฒด์งํฅ ํ๋ก๊ทธ๋๋ฐ
ํจ์ํ๊ณผ ๊ฐ์ฒด์งํฅ, ๋ญ๊ฐ ๋ ์ข์๊น? ์ฌ์ค ์ ๋ต์ ์์ด. ์ํฉ์ ๋ฐ๋ผ ์ ์ ํ ์์ด ์ฐ๋ ๊ฒ ์ต์ ์ด์ผ! ๐จ
๐ฏ ์ธ์ ํจ์ํ์ ์ฌ์ฉํ ๊น?
โ
ํจ์ํ์ด ์ข์ ๊ฒฝ์ฐ:
โข ๋ฐ์ดํฐ ๋ณํ ํ์ดํ๋ผ์ธ
โข ๋ถ๋ณ ๋ฐ์ดํฐ ์ฒ๋ฆฌ
โข ๋ณ๋ ฌ ์ฒ๋ฆฌ๊ฐ ํ์ํ ๊ฒฝ์ฐ
โข ํ
์คํธ๊ฐ ์ค์ํ ๋น์ฆ๋์ค ๋ก์ง
โข ์ํ ๊ด๋ฆฌ (Redux ์คํ์ผ)
โ
๊ฐ์ฒด์งํฅ์ด ์ข์ ๊ฒฝ์ฐ:
โข UI ์ปดํฌ๋ํธ
โข ๋ณต์กํ ์ํ๋ฅผ ๊ฐ์ง ๊ฐ์ฒด
โข ์์์ด ํ์ํ ๊ฒฝ์ฐ
โข ํ๋ ์์ํฌ ํตํฉ (UIKit, SwiftUI)
// ํ์ด๋ธ๋ฆฌ๋ ์ ๊ทผ๋ฒ - ๋ ๋ค ํ์ฉ!
// 1. ๋ถ๋ณ ๋ฐ์ดํฐ ๋ชจ๋ธ (ํจ์ํ)
struct User {
let id: Int
let name: String
let email: String
}
// 2. ์์ ํจ์๋ก ๋น์ฆ๋์ค ๋ก์ง (ํจ์ํ)
func validateUser(_ user: User) -> Result<user, validationerror=""> {
// ๊ฒ์ฆ ๋ก์ง...
return .success(user)
}
func transformToViewModel(_ user: User) -> UserViewModel {
return UserViewModel(displayName: user.name, email: user.email)
}
// 3. ํด๋์ค๋ก ์ํ ๊ด๋ฆฌ (๊ฐ์ฒด์งํฅ)
class UserManager {
private var users: [User] = []
// ํจ์ํ ์คํ์ผ์ ๋ฉ์๋
func addUser(_ user: User) -> Result<void, error=""> {
return validateUser(user).map { validUser in
self.users.append(validUser)
}
}
func getUsers(matching predicate: (User) -> Bool) -> [User] {
return users.filter(predicate)
}
func transformUsers<t>(_ transform: (User) -> T) -> [T] {
return users.map(transform)
}
}
// 4. SwiftUI ๋ทฐ (์ ์ธ์ + ํจ์ํ)
struct UserListView: View {
let users: [User]
var body: some View {
List(users, id: \.id) { user in
UserRow(user: user)
}
}
}
// ์ต๊ณ ์ ์กฐํฉ: ๊ฐ ํจ๋ฌ๋ค์์ ์ฅ์ ์ ํ์ฉ!</t></void,></user,>
๐ก ์ค๋ฌด ํ๊ณผ ๋ฒ ์คํธ ํ๋ํฐ์ค
ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ์ค๋ฌด์ ์ ์ฉํ ๋ ์์๋๋ฉด ์ข์ ํ๋ค์ ์ ๋ฆฌํด๋ดค์ด! ๐ฏ
1. ์ ์ง์ ์ผ๋ก ๋์ ํ๊ธฐ
์ฒ์๋ถํฐ ๋ชจ๋ ์ฝ๋๋ฅผ ํจ์ํ์ผ๋ก ์์ฑํ๋ ค๊ณ ํ์ง ๋ง. ๋จผ์ ๋ฐ์ดํฐ ๋ณํ ๋ก์ง๋ถํฐ ์์ํด์ ์ ์ง์ ์ผ๋ก ํ๋ํด๋๊ฐ๋ ๊ฒ ์ข์.
2. ๊ฐ๋ ์ฑ ์ฐ์
ํจ์ํ ์คํ์ผ์ด ํญ์ ๋ ์งง์ ๊ฑด ์๋์ผ. ๋๋ก๋ ๋ช ๋ นํ ์คํ์ผ์ด ๋ ์ฝ๊ธฐ ์ฌ์ธ ์ ์์ด. ์ฝ๋์ ์๋๊ฐ ๋ช ํํ ๊ฒ ๊ฐ์ฅ ์ค์ํด!
// ๊ณผ๋ํ๊ฒ ํจ์ํ - ์ฝ๊ธฐ ์ด๋ ค์
let result = users
.filter { $0.age >= 18 }
.map { ($0.name, $0.email) }
.reduce(into: [String: String]()) { $0[$1.0] = $1.1 }
// ๋ ๋์ ๋ฐฉ๋ฒ - ์๋๊ฐ ๋ช
ํ
let adultUsers = users.filter { $0.age >= 18 }
let emailMap = Dictionary(
uniqueKeysWithValues: adultUsers.map { ($0.name, $0.email) }
)
3. ์ ์ ํ ์ถ์ํ ์์ค
๋๋ฌด ๋ง์ ์ถ์ํ๋ ์คํ๋ ค ์ฝ๋๋ฅผ ๋ณต์กํ๊ฒ ๋ง๋ค์ด. ์ค์ฉ์ ์ธ ์์ค์์ ๋ฉ์ถ๋ ๊ฒ ์ค์ํด.
4. ๋ฌธ์ํ์ ํ์ ๋ช ์
ํจ์ํ ์ฝ๋๋ ๋๋ก ํ์ ์ถ๋ก ์ด ๋ณต์กํด์ง ์ ์์ด. ์ค์ํ ํจ์์๋ ํ์ ์ ๋ช ์ํ๊ณ ๋ฌธ์๋ฅผ ์์ฑํ์.
/// ์ฌ์ฉ์ ๋ชฉ๋ก์ ํํฐ๋งํ๊ณ ๋ณํํฉ๋๋ค.
/// - Parameters:
/// - users: ํํฐ๋งํ ์ฌ์ฉ์ ๋ฐฐ์ด
/// - predicate: ํํฐ๋ง ์กฐ๊ฑด
/// - transform: ๋ณํ ํจ์
/// - Returns: ๋ณํ๋ ๊ฒฐ๊ณผ ๋ฐฐ์ด
func processUsers<t>(
_ users: [User],
filter predicate: (User) -> Bool,
transform: (User) -> T
) -> [T] {
return users
.filter(predicate)
.map(transform)
}</t>
5. ์๋ฌ ์ฒ๋ฆฌ ๋ช ํํ
ํจ์ํ ์คํ์ผ์์๋ ์๋ฌ ์ฒ๋ฆฌ๋ ์ค์ํด. Result ํ์ ์ด๋ throws๋ฅผ ์ ์ ํ ํ์ฉํ์.
6. ํ๊ณผ ์ํตํ๊ธฐ
ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ์ต์ํ์ง ์์ ํ์์ด ์๋ค๋ฉด, ์ฝ๋ ๋ฆฌ๋ทฐ๋ ํ์ด ํ๋ก๊ทธ๋๋ฐ์ ํตํด ํจ๊ป ๋ฐฐ์๋๊ฐ๋ ๊ฒ ์ข์. ํผ์๋ง ์๋ ์ฝ๋๋ ์ข์ ์ฝ๋๊ฐ ์๋์ผ!
๐ ๋ ๋์๊ฐ๊ธฐ
ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ๋ ๊น์ด ๊ณต๋ถํ๊ณ ์ถ๋ค๋ฉด? ์ด๋ฐ ๊ฒ๋ค์ ์์๋ณด๋ฉด ์ข์! ๐
๐ ์ถ๊ฐ ํ์ต ์ฃผ์
1. ๋ชจ๋๋(Monad)
Optional, Result, Array ๋ฑ์ด ๋ชจ๋ ๋ชจ๋๋์ผ. ๋ชจ๋๋ ํจํด์ ์ดํดํ๋ฉด ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ด ๋ ๋ช
ํํด์ ธ.
2. ํจ์ ์กฐํฉ(Function Composition)
์์ ํจ์๋ค์ ์กฐํฉํด์ ํฐ ํจ์๋ฅผ ๋ง๋๋ ๊ธฐ๋ฒ. ์ฝ๋ ์ฌ์ฌ์ฉ์ฑ์ด ๊ทน๋ํ๋ผ.
3. ๋ ์ฆ(Lens)
๋ถ๋ณ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ฅผ ๋ค๋ฃจ๋ ๊ณ ๊ธ ๊ธฐ๋ฒ. ์ค์ฒฉ๋ ๊ตฌ์กฐ์ฒด๋ฅผ ์ฐ์ํ๊ฒ ์
๋ฐ์ดํธํ ์ ์์ด.
4. ๋ฆฌ์กํฐ๋ธ ํ๋ก๊ทธ๋๋ฐ
Combine์ด๋ RxSwift ๊ฐ์ ํ๋ ์์ํฌ๋ก ํจ์ํ ๋ฆฌ์กํฐ๋ธ ํ๋ก๊ทธ๋๋ฐ์ ๋ฐฐ์๋ณด์.
5. ํ์
์ฃผ๋ ๊ฐ๋ฐ
ํ์
์์คํ
์ ํ์ฉํด์ ์ปดํ์ผ ํ์์ ๋ฒ๊ทธ๋ฅผ ์ก๋ ๊ธฐ๋ฒ.
๐ ํ์ต ๋ฆฌ์์ค
โข Apple์ Swift ๊ณต์ ๋ฌธ์
โข "Functional Swift" ์ฑ
โข Point-Free ๋น๋์ค ์๋ฆฌ์ฆ
โข Swift by Sundell ๋ธ๋ก๊ทธ
โข ๊ทธ๋ฆฌ๊ณ ์ฌ๋ฅ๋ท์์ ์ค์ํํธ ์ ๋ฌธ๊ฐ๋ค์ ์ง์์ ๊ณต์ ๋ฐ์ ์๋ ์์ด!
์ฌ๋ฅ๋ท(https://www.jaenung.net)์๋ ์ค์ํํธ์ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ๋ํ ๋ค์ํ ์ง์๊ณผ ๊ฒฝํ์ ๊ฐ์ง ๊ฐ๋ฐ์๋ค์ด ๋ง์. ๊ถ๊ธํ ๊ฒ ์๋ค๋ฉด ์ง์์ธ์ ์ฒ์ ๋๋ฌ๋ณด๊ฑฐ๋, ์ง์ ์ง๋ฌธํด๋ณด๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ผ! ๐ณ
๐ฌ ๋ง๋ฌด๋ฆฌํ๋ฉฐ
์, ์ฌ๊ธฐ๊น์ง ์ค์ํํธ์ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ๊ณผ ๋ถ๋ณ์ฑ, ์์ํจ์ ํจํด์ ๋ํด ์์๋ดค์ด! ์ ๋ง ๊ธด ์ฌ์ ์ด์์ง? ๐
ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ์ฒ์์๋ ๋ฏ์ค๊ณ ์ด๋ ค์ธ ์ ์์ด. ํ์ง๋ง ์กฐ๊ธ์ฉ ์ฐ์ตํ๋ค ๋ณด๋ฉด ์ ๋ง ๊ฐ๋ ฅํ ๋๊ตฌ๋ผ๋ ๊ฑธ ์๊ฒ ๋ ๊ฑฐ์ผ. ์ฝ๋๊ฐ ๋ ์์ ํด์ง๊ณ , ํ ์คํธํ๊ธฐ ์ฌ์์ง๊ณ , ๋ฒ๊ทธ๊ฐ ์ค์ด๋๋ ๊ฑธ ๊ฒฝํํ๊ฒ ๋ ๊ฑฐ์ผ.
๐ฏ ํต์ฌ ์์ฝ
์์ํจ์: ๊ฐ์ ์ ๋ ฅ์ ํญ์ ๊ฐ์ ์ถ๋ ฅ, ๋ถ์์ฉ ์์
๋ถ๋ณ์ฑ: ๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝํ์ง ์๊ณ ์๋ก์ด ๋ฐ์ดํฐ ์์ฑ
๊ณ ์ฐจํจ์: ํจ์๋ฅผ ์ธ์๋ก ๋ฐ๊ฑฐ๋ ๋ฐํํ๋ ํจ์
ํจ์ ์กฐํฉ: ์์ ํจ์๋ค์ ์กฐํฉํด์ ๋ณต์กํ ๋ก์ง ๊ตฌํ
์ค์ฉ์ฑ: ์ํฉ์ ๋ง๊ฒ ํจ์ํ๊ณผ ๊ฐ์ฒด์งํฅ์ ์ ์ ํ ์์ด ์ฌ์ฉ
๊ฐ์ฅ ์ค์ํ ๊ฑด ์ค์ ๋ก ์ฝ๋๋ฅผ ์์ฑํด๋ณด๋ ๊ฒ์ด์ผ. ์ด๋ก ๋ง ์์์๋ ์์ฉ์์ด. ์์ ํ๋ก์ ํธ๋ถํฐ ์์ํด์ ํจ์ํ ์คํ์ผ์ ์ ์ฉํด๋ณด์. ์ฒ์์๋ ์ด์ํ๊ฒ ์ง๋ง, ์ ์ ์์ฐ์ค๋ฌ์์ง ๊ฑฐ์ผ! ๐ช
๊ทธ๋ฆฌ๊ณ ํผ์ ๊ณต๋ถํ๊ธฐ ์ด๋ ต๋ค๋ฉด, ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ๋ค๋ฅธ ๊ฐ๋ฐ์๋ค๊ณผ ์ํตํ๋ฉด์ ๋ฐฐ์ฐ๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ผ. ํจ๊ป ์ฑ์ฅํ๋ ๊ฒ ๊ฐ์ฅ ๋น ๋ฅธ ๊ธธ์ด๊ฑฐ๋ ! ๐ฑ
๐ ๋ค์ ๋จ๊ณ
1. ์ค๋ ๋ฐฐ์ด ๋ด์ฉ์ ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํด๋ณด๊ธฐ
2. map, filter, reduce๋ฅผ ์์์ ์ผ๋ก ์ฌ์ฉํด๋ณด๊ธฐ
3. ๋ถ๋ณ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ก ๋ชจ๋ธ ์ค๊ณํด๋ณด๊ธฐ
4. ์์ ํจ์๋ก ๋น์ฆ๋์ค ๋ก์ง ๋ถ๋ฆฌํด๋ณด๊ธฐ
5. ํ
์คํธ ์ฝ๋ ์์ฑํด๋ณด๊ธฐ
ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ๋จ์ํ ์ฝ๋ฉ ์คํ์ผ์ด ์๋๋ผ, ๋ ๋์ ์ํํธ์จ์ด๋ฅผ ๋ง๋ค๊ธฐ ์ํ ์ฌ๊ณ ๋ฐฉ์์ด์ผ. ์ด ์ฌ๊ณ ๋ฐฉ์์ ์ตํ๋ฉด ์ด๋ค ์ธ์ด๋ฅผ ์ฌ์ฉํ๋ ๋ ์ข์ ์ฝ๋๋ฅผ ์์ฑํ ์ ์๊ฒ ๋ ๊ฑฐ์ผ.
์, ์ด์ ์ง์ ์ฝ๋๋ฅผ ์์ฑํ๋ฌ ๊ฐ๋ณผ๊น? ํ์ดํ ! ๐โจ
๐ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ์ธ๊ณ์ ์ค์ ๊ฑธ ํ์ํฉ๋๋ค! ๐
๋ ์์ ํ๊ณ , ๋ ์์ธก ๊ฐ๋ฅํ๊ณ , ๋ ์๋ฆ๋ค์ด ์ฝ๋๋ฅผ ํฅํด! ๐ซ
๊ด๋ จ ํค์๋
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.

๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.