don't stop believing

Reduce 사용하기 본문

Swift/Basic

Reduce 사용하기

Tongchun 2018. 2. 19. 16:06

Reduce기능은 사실 결합이라고 불려야 마땅한 기능입니다. 리듀스는 컨테이너 내부의 콘텐츠를 하나로 합하는 기능을 실행하는 고차합수입니다. 배열이라면 배열의 모든 값을 전달인자로 전달받은 클로저의 연살 결과로 합해줍니다.


Swift의 리듀스는 두 가지 형태로 구현되어 있습니다. 첫 번째 Reduce는 클로저가 각 요소를 전달받아 연산한 후 값을 다시 클로저 실행을 위해 반환하며 컨테이너를 순환하는 형태입니다.

public func reduce<Result>(_ initialResult: Result, 

_ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result

initialResult이라는 이름의 매개별수로 전달되는 값을 통해 초깃값을 지정해 줄 수 있으며, nextPartialResult라는 이름의 매개변수로 클로저를 전달받습니다. nextPartialResult 클로저의 첫 번째 매개별수는 Reduce 메서드의 initialResult 매개변수를 통해 전달받은 초깃값 또는 이전 클로저의 결괏값입니다. 모든 순회가 끝나면 Reduce의 최종 결괏값이 됩니다. 두 번째 매개변수는 Reduce 메서드가 순환하는 컨테이너의 요소입니다.


두 번째 Reduce 메서드는 컨테이너를 순환하며 클로저가 실행되지만 클로저가 따로 결괏값을 반환하지 않는 형태입니다. 대신 inout 매개변수를 사용하여 초깃값에 직접 연산을 실행하게 됩니다.

public func reduce<Result>(into initialResult: Result,

_ updateAccumulatingResult: (inout Result, Element) throws -> () rethrows -> Result)

updateAccumulatingResult 매개변수로 전달받는 클로저의 매개변수 중 첫 번째 매개변수를 inout 매개변수로 사용합니다. updateAccumulatingResult 클로저의 첫 번째 매개 변수는 Reduce 메서드의 initialResult 매개별수를 이용해 전달받은 초깃값 또는 이전에 실행된 클로저 때문에 변경되어 있는 결괏값입니다. 모든 순회가 끝나면 리듀스의 최종 결괏값이 됩니다. 두 번째 매개변수는 Reduce 메서드가 순환하는 컨테이너의 요소입니다. 상황에 따러서는 Reduce를 Map과 유사하게 사용할 수도 있습니다.


첫 번째 형태인 reduce(_: _:) 메서드의 사용 코드입니다. 초깃값이 0이고 정수 배열의 모든 값을 더합니다.

let numbers: [Int] = [1, 2, 3]

var sum: Int = numbers.reduce(0, { (result: Int, element: Int) -> Int in
    print("\(result) + \(element)")
    // 0 + 1
    // 1 + 2
    // 3 + 3
    return result + element
})
print(sum)  // 6


초깃값이 0이고 정수 배열의 모든 값을 뺍니다.

let numbers: [Int] = [1, 2, 3]

var sum: Int = numbers.reduce(0, { (result: Int, element: Int) -> Int in
    print("\(result) - \(element)")
    // 0 - 1
    // -1 - 2
    // -3 - 3
    return result - element
})
print(sum)  // -6


초깃값이 3이고 정수 배열의 모든 값을 더합니다. Closure 표현식을 사용해 간결하게 합니다.

let numbers: [Int] = [1, 2, 3]

var sumFromThree: Int = numbers.reduce(3) {
    print("\($0) + \($1)")
    // 3 + 1
    // 4 + 2
    // 6 + 3
    return $0 + $1
}
print(sumFromThree) // 9


초깃값이 3이고 정수 배열의 모든 값을 뺍니다. Closure 표현식을 사용해 간결하게 합니다.

let numbers: [Int] = [1, 2, 3]

var subtractFromThree: Int = numbers.reduce(3) {
    print("\($0) - \($1)")
    // 3 - 1
    // 2 - 2
    // 0 - 3
    return $0 + $1
}
print(subtractFromThree)    // -3

문자열 배열을 reduce(_: _:) 메서드를 이용해 연결시킬수 있습니다.

let names: [String] = ["Chope", "Jay", "Joker", "Nova"]

let reduceNames: String = names.reduce("tongchun's friend : ") {
    return $0 + ", " + $1
}
print(reduceNames)  // tongchun's friend : "Chope", "Jay", "Joker", "Nova"


두 번째 형태인 reduce(into: _:) 메서드를 사용해 보겠습니다. 

초깃값이 0이고 정수 배열의 모든 값을 더합니다. 첫 번째 Reduce 형태와 달리 Closure의 값을 반환하지 않고 내부에서 직접 이전 값을 변경한다는 점이 다릅니다.

let numbers: [Int] = [1, 2, 3]

var sum = numbers.reduce(into: 0, { (result: inout Int, element: Int) in
    // 0 + 1
    // 1 + 2
    // 3 + 3
    print("\(result) + \(element)")
    result += element
})
print(sum)  // 6


초깃값이 3이고 정수 배열의 모든 값을 뺍니다.

let numbers: [Int] = [1, 2, 3]

var sum = numbers.reduce(into: 3, { (result: inout Int, element: Int) in
    // 0 + 1
    // 1 + 2
    // 3 + 3
    print("\(result) - \(element)")
    result -= element
})
print(sum)  // -3


첫 번째 Reduce 형태와 다르기 때문에 다른 컨테이너에 값을 변경하여 넣어줄 수도 있습니다. 이렇게 하면 맵이나 필터와 유사한 형태로 사용할 수도 있습니다.

홀수는 걸러내고 짝수만 두 배로 변경하여 초깃값인 [1, 2, 3] 배열에 직접 연산합니다.

let numbers: [Int] = [1, 2, 3]

var doubledNumbers: [Int] = numbers.reduce(into: [1, 2]) {(result: inout [Int], element: Int) in
    print("reslt: \(result) element: \(element)")
    // result: [1, 2] element: 1
    // result: [1, 2] element: 2
    // result: [1, 2, 4] element: 3

    guard element % 2 == 0 else {
        return
    }

    print("\(result) append \(element)")
    // [1, 2] append 2

    result.append(element * 2)
}

print(doubledNumbers)   // [1, 2, 4]

위 Reduce 코드를 Map과 Filter를 사용한 코드입니다.

let numbers: [Int] = [1, 2, 3]

let doubledNumber = [1, 2] + numbers.filter { $0 % 2 == 0 }.map { $0 * 2}

print(doubledNumber)    // [1, 2, 4]


이름을 모두 대문자로 변환하여 초깃값인 빈 배열에 직접 연산합니다.

let names: [String] = ["Chope", "Jay", "Joker", "Nova"]

var upperCasedName: [String]
upperCasedName = names.reduce(into: [], {
    $0.append($1.uppercased())
})

print(upperCasedName)   // ["CHOPE", "JAY", "JOKER", "NOVA"]


Map을 사용한 대문자 변환은 아래와 같습니다.

let names: [String] = ["Chope", "Jay", "Joker", "Nova"]

var upperCasedName: [String]
upperCasedName = names.map { $0.uppercased() }

print(upperCasedName)   // ["CHOPE", "JAY", "JOKER", "NOVA"]


'Swift > Basic' 카테고리의 다른 글

Filter 사용하기  (0) 2018.02.19
Map 사용하기  (0) 2018.02.19
탈출 클로저 @escaping  (0) 2018.01.31
defer (후처리)  (0) 2018.01.24
Swift Access Control  (0) 2018.01.19
Comments