ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Swift - reduce(_:_:)와 reduce(into:_:)
    Programming/Swift 2022. 1. 29. 15:39

    안녕하세요 BeePeach입니다 :>

     

    오늘 공부해볼 내용은 reduce 메서드입니다.

    reduce 메서드는 Container의 요소를 결합하는 작업을 수행할 때 사용합니다.

     

    그럼 reduce 메서드를 사용하는 방법과 reduce(_:_:)와 reduce(into:_:)는 어떤 차이를 가지는지 같이 공부해보도록 하겠습니다.

     

     


     

    reduce(_:_:)

    선언 부분을 보면 Generic function인것을 확인할 수 있습니다.

    타입 파라미터로 Result가 선언되어 있네요.

    타입을 확인하기 어렵게 되어있는데 찬찬히 하나씩 본다면 어렵지 않습니다.

     

    첫 번째 파라미터는 initialResult 즉, 초기값이 전달됩니다.

    뜬금없이 초기값이 뭔데??라고 할 수 있는데 예제를 보시게 되면 바로 이해하실 수 있습니다.

     

    두 번째 파라미터는 nextPartialResult 이름을 가진  (Result, Self.Element) -> Result 타입의 클로저가 전달됩니다.

    이름으로 유추해보자면 부분적인 결과인 거 같네요.

    이것도 일단 뭔 말인지는 모르지만 다음을 보겠습니다.

     

    이번에는 두 번째 파라미터로 전달된 클로저를 좀 더 자세히 보겠습니다.

    클로저의 첫번째 파라미터는 initialResult로 전달받은 초기값 또는 이전 클로저가 반환하는 return값이 전달됩니다.

    클로저의 두 번째 파라미터는 reduce(_:_:)를 호출한 컨테이너의 요소가 전달됩니다.

    클로저가 반환하는 값은 파라미터로 받은 두 값을 적절하게 처리하여 첫 번째 파라미터로 받은 값과 같은 타입의 값을 리턴해줍니다.

     

    마지막으로 reduce(_:_:) 메서드가 자체가 Result타입을 리턴하고 있습니다.

    이 결과값은 클로저에서 정의한 연산을 끝내고 return 한 값을 반환해줍니다.

     

    풀어서 설명하자면 클로저의 첫번째 파라미터에 맨 처음 initialResult로 전달된 초기값이 전달되고 두 번째 파라미터에 컨테이너의 맨 첫 요소가 전달됩니다.

    그리고 이 두 파라미터를 이용해서 클로저에서 정의한 코드를 실행한 후에 결괏값을 리턴해주면

    이 리턴값을 다시 클로저의 첫 번째 파라미터로 전달하고 두 번째 파라미터에는 컨테이너의 두 번째 요소가 전달됩니다.

    이렇게 컨테이너의 모든 요소가 전달될때까지 이 클로저가 반복됩니다.

    쉽게 Recursive function을 생각하면 됩니다.

    그래서 이름이 nextPartialResult입니다. 부분적인 결과들을 리턴하고 이 결과를 이용해서 다음 연산에 사용하는 것입니다.

     

    처음에 설명만 들으면 이게 뭔 소리야??하기 쉽습니다.

    예제를 보게되면 금방 이해할 수 있습니다.

     

     

    예시를 보면 [1, 2, 3] 배열에서 reduce(_:_:) 메서드를 호출했습니다.

    sum은 초기값으로 0을 전달하고 클로저에서 첫 번째 파라미터인 result와 두 번째 파라미터인 element를 더한 값을 리턴하고 있습니다.

     

    전달된 클로저의 흐름을 살펴보겠습니다.

    첫 번째 파라미터인 result에는 초기값인 0이 전달됩니다.

    두 번째 파라미터인 element에는 첫 요소의 값인 1이 전달됩니다.

    이 두 값을 더해서 1이라는 결과가 나오게 됩니다.

     

    그럼 이 1이라는 결과는 다시 클로저의 첫 번째 파라미터인 result로 전달됩니다.

    두 번째 파라미터에는 element의 두 번째 요소인 2가 전달됩니다.

    이 두 값을 더해서 3이라는 결과가 나오게 됩니다.

    이러한 과정을 reduce(_:_:)를 호출한 컨테이너의 요소를 모두 반복될 때까지 진행합니다.

     

     

    예제를 한 가지 더 보도록 하겠습니다.

    이번에는 각 요소를 뺀 결과를 얻을 수 있습니다.

    차이점은 초기 값이 10입니다. 그래서 10에 각 요소인 1, 2, 3을 반복적으로 빼게 됩니다.

     

    클로저를 생략한다면 더 쉽게 표현이 가능합니다.

     

     


     

    reduce(into:_:)

    그럼 이 메서드는 reduce(_:__:)와 무슨 차이일까요??

     

    첫번째 파라미터는 initialResult로 reduce(_:_:) 메서드와 똑같은 의미를 가집니다.

    초기값을 의미하죠!

     

    두 번째 파라미터는 타입이 (inout Result, Element) -> Void)인 클로저를 받습니다.

    이름은 updateAccumulatingResult입니다. 누적된 결과를 업데이트한다는 의미 같네요??

    클로저의 첫 번째 파라미터는 initialResult로 전달받은 초기값 또는 이전 클로저 실행으로 변경된 누적 결괏값입니다.

    클로저의 두 번째 파라미터는 reduce(into:_:) 메서드를 호출한 컨테이너의 요소가 전달됩니다.

    여기서 주의 깊게 봐야 하는 부분은 첫 번째 파라미터가 inout파라미터인 점과 리턴 값이 없다는 점입니다.

     

    reduce(into:_:)메서드 자체는 클로저의 연산 결과를 반복적으로 거친 Result를 리턴합니다.

     

    reduce(into:_:)의 가장 큰 차이는 클로저의 첫 번째 파라미터가 inout 파라미터라는 점입니다.

    즉, initialResult로 전달받은 값을 클로저안에서 직접 변경합니다.

    reduce(_:_:)은 initialResult로 전달 받은 값을 클로저에서 변경할 수 없습니다.

     

    먼저 reduce(_:_:)에서 사용한 것과 비슷한 예제를 보겠습니다.

     

     

    똑같이 요소들을 더하고 빼는 작업들입니다.

    클로저를 보면 return 대신 +=, -=를  이용하는 것을 볼 수 있습니다.

    클로저가 값을 리턴하는 게 아니고 첫 번째 파라미터로 전달된 값을 직접 변경하고 사용하기 때문에 첫 번째 파라미터에 값을 누적시켜주면 됩니다.

     

    흐름을 따라가 본다면 초기값으로 10이 전달되고 이 초기값은 클로저의 result로 전달됩니다.

    초기값에 10이 전달되고 이 값과 컨테이너의 첫 요소인 1을 더해서 이 결과를 다시 result에 저장합니다.

    클로저가 끝나면 다시 클로저가 호출됩니다. 이번에는 result에 이전에 저장한 11이 저장되어 있습니다.

    11과 컨테이너의 두 번째 요소인 2를 더한 값을 다시 result에 저장합니다.

    이러한 과정을 반복하면서 작업을 수행하게 됩니다.

    reduce(_:_:), reduce(into:_:) 두 메서드의 차이가 크게 없어 보입니다.

     

     


     

    reduce(_:_:), reduce(into:_:)의 차이

     

    클로저의 파라미터가 inout이라는 것은 무엇을 다르게 만들어줄까요??

    만약 initialResult로 Array, Dictionary를 받는다고 생각해봅시다.

    그럼 reduce(_:_:)에서는 Array, Dictionary를 직접 수정할 수 없기 때문에 새로운 변수로 copy 한 다음에 그 값을 변경하고 사용해야 하는 번거로움이 있습니다.

     

     

    이 예제는 초기값으로 [-2, -1, 0]인 배열을 전달해서 reduce(_:_:)를 호출한 컨테이너의 요소들을 초기값으로 전달된 배열에 넣어주는 작업을 하고 있습니다.

    이때 초기값으로 전달된 배열을 직접 수정할 수 없기 때문에 바로 append를 호출할 수 없습니다.

    그래서 새로운 변수를 생성하고 여기에 append를 한 뒤 그 결과를 리턴하고 있습니다.

     

     

    이렇게 직접 수정하는 방법은 애초에 사용이 불가능합니다.

    그럼 이제 reduce(into:_:)를 이용해볼까요??

     

     

     

    reduce(into:_:)는 initialResult를 Array, Dictionary로 받아도 클로저에서 직접 변경이 가능하므로 데이터를 다루기 쉬워집니다.

    위와 같은 코드이지만 이번에는 훨씬 간단한 것을 확인할 수 있습니다.

    Array를 클로저 내부에서 직접 수정이 가능하니까요!

     

    initialResult를 Array나 Dictionary로 받는 게 왜 중요하냐고요??

    reduce() 메서드의 결괏값이 결국에는 initialResult의 타입과 같아야 하기 때문입니다.

    reduce(into:_:) 메서드는 초기값을 Array, Dictionary으로 전달하고 결과도 이 형태로 받을 수 있습니다.

    아주 유용한 예시를 보도록 하겠습니다.

     

     

    이 예시를 보면 String에서 reduce(into:_:)를 호출하고 있습니다.

    String에서도 reduce 메서드를 사용할 수 있습니다.

     

    초기 값으로 [:] 빈 Dictionary를 전달했습니다.

    그럼 이제 reduce(into:_:) 메서드의 결과는 Dictionary형태가 되어야 합니다.

     

    클로저에서 counts에는 처음에 초기값인 [:]이 전달됩니다.

    letter는 String의 각각 Character가 전달됩니다.

    counts[letter]는 현재 없는 key이므로 원래는 nil이 리턴되어야 하죠??

    하지만 Dictionary에는 subscript로 default값을 전달할 수 있었습니다.

    letter key가 없었다면 기본값을 0으로 하고 1을 더합니다.

    그럼 character를 key로 가지고 value로는 개수를 가지는 [Character: Int] Dictionary를 얻을 수 있습니다.

    이렇게 "abracadabra"에 각 char들이 몇 번 들어가 있는지 알 수 있는 dictionary를 얻었습니다.

     

     


     

    정리

     

    reduce(_:_) 메서드는 초기값으로 Int, String과 같이 단일 값을 받은 뒤  요소들을 모두 합해서 단일 값을 구하는데 유용합니다.

    맨 위에서 보았던 요소들의 전체 합을 얻을 때와 같은 경우입니다.

     

    reduce(into:_) 메서드는  Array, Dictionary으로 된 결괏값을 얻고 싶을 때 사용합니다.

    맨 마직으로 살펴본 예제와 같은 경우입니다.

     

     

     


     

    참고 자료

     

    https://developer.apple.com/documentation/swift/array/3126956-reduce

     

    Apple Developer Documentation

     

    developer.apple.com

     

     

    https://developer.apple.com/documentation/swift/array/2298686-reduce

     

    Apple Developer Documentation

     

    developer.apple.com

     

     

    http://www.kyobobook.co.kr/product/detailViewKor.laf?ejkGb=KOR&mallGb=KOR&barcode=9791162242223 

     

    스위프트 프로그래밍: Swift 5 - 교보문고

    객체지향, 함수형, 프로토콜 지향 패러다임까지 한 번에! | 스위프트를 제대로 이해하고 싶은 개발자를 위한 책스위프트는 iOS와 macOS용 애플리케이션 개발에 주로 사용하는 프로그래밍 언어입니

    www.kyobobook.co.kr

     

     


     

     

    728x90

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

    Swift - Error Handling 기초  (0) 2022.03.24
    Swift - UserDefaults에 customType 저장하기  (0) 2022.03.01
    Swift - Generic Type Constraint  (0) 2022.01.25
    Swift - Generic Type 확장하기  (0) 2022.01.24
    Swift - Associated Type  (0) 2022.01.19
Designed by Tistory.