compactMap - 함수를 인자로 받는 functional programming paradigm의 좋은 예

let bands = "yellow violet red gold"
let colors = ["black", "brown", "red", "orange", "yellow", "green", "blue", "violet", "gray", "white"]

let names = bands.components(separatedBy: " ")

let digits = names.compactMap(colors.firstIndex) // (1)

let digits = names.compactMap{ name in colors.firstIndex(of: name) } // (2)

print(digits)

/*
names.compactMap(transform: (String) throws -> ElementOfResult?) (3)
colors.firstIndex(of element: String) -> Int?  (4)
*/

compactMap 을 자주 사용한다. 배열을 가공해야할 때 처리와 동시에 옵셔널을 벗겨내기 때문에 유용하다.

그런데 오늘 (1) 과 같은 사용법을 처음 보았다. 분명 보면 뭔가 이해가 되는 것 같으면서도 감을 잡기 어려웠다.

그래서 엑스코드 플레이그라운드에서 깨작거리면서 나름대로의 궁금증을 해소했다.

그러니까 처음에 (1)과 (2)는 똑같은 결과물을 반환하니까 완전히 같은 동작을 하는 줄 알았고, 표현 방식만 다른 줄 알았다. 그런데 자세히 보니까 살짝 다른 부분이 있었다.

잠시 (3) 을 살펴보면 names.compactMap 의 인자값이 어떻게 들어와야하는지 나와있다.

(String) throws → ElementOfResult? , 즉 문자열 타입의 인자 하나와 ElementOfResult?라는 타입을 반환하는 함수 혹은 클로져를 받는다.

이제 (4) 를 보면, (3)에서 인자로 받는 함수/클로저와 colors.firstIndex의 인자와 반환형태가 동일한 것을 볼 수 있다.

그러니까, (1)에서는 firstIndex 함수를 인자로 넣는 것이고, (2) 에서는 클로저를 인자로 넣고, 그 클로져에서 firstIndex를 실행하고 값을 반환하는 것이다.

(1)의 클로저 자체가 firstIndex인 셈이다.

함수형 프로그래밍 패러다임의 코드는 조금 뚫어져라 봐야하는 경향이 있지만 막상 이해하고 나면 참 재밌고 아름다운 것 같다.

DropFirst

보통은 Dropfirst가 배열의 가장 첫 요소를 없애는 걸로 사용한다.