for - in / forEach 차이점
why?
코드로 작업을 하는 도중 문득, for-in과 forEach의 정확한 차이점이 뭔지 궁금해졌습니다.
뭔가.. 정확한 의미 없이 쓰는 느낌(?)이 좀 들어서, 이번 포스팅 기회로 정확하게 알고자 공부하였습니다.
모든 collection을 비교하는 건, 내용이 너무 길어질 것 같아서
비교적 가장 많이 쓰이는 배열(Array)를 대표적인 예시로 설명해보겠습니다.
(모든 예제코드는 playground로 작성되었으며, 하단의 주석은 결과값을 나타냅니다.)
1. for - in (Array)
컬렉션(여기선 Array)에 저장된 요소 수만큼 반복되며, 저장된 요소가 루프 상수에 하나씩 들어간다.
끊어서 차근차근 읽어보겠습니다.
컬렉션(=Array)에 / 저장된 요소 수만큼 / 반복 / 되며
저장된 요소가 / 루프 상수에 / 하나씩 / 들어간다.
(여기서 루프 상수란, for와 in 사이에 개발자가 선언하는 상수를 의미합니다. 보통 i를 많이 사용하죠.)
예시 코드를 하나 보겠습니다.
let apple: [String] = ["iPhone", "iPad", "Macbook","AirPods"]
for i in apple {
print(i)
}
// ==== console
// iPhone
// iPad
// Macbook
// AirPods
아주 간단한 코드죠?
apple이란 배열을 for - in 함수에 반복시킨 결과를 나타내고 있습니다.
이제 이 코드를 천천히 해석해봅시다.
for - in 정의를 대입해서 다시 살펴보면,
"apple이란 배열(array)가 있을 때, apple의 요소 갯수( = 총 갯수) 만큼 반복한다."
라고 해석해볼 수 있습니다.
1번째 반복: i란 루프 상수에 apple[0] -> "iPhone"
2번째 반복: i란 루프 상수에 apple[1] -> "iPad"
3번째 반복: i란 루프 상수에 apple[2] -> "Macbook"
4번째 반복: i란 루프 상수에 apple[3] -> "AirPods"
그럼 n번째 반복은? -> i란 루프 상수에 apple[n-1] 이라고 생각할 수 있겠네요.
이제 forEach함수를 살펴봅시다.
2. forEach (Array)
반복 실행하려는 코드를 파라미터로 받고, 저장된 요소는 클로저 상수로 전달된다.
여기서 가장 주목해야할 부분은, '전달'이라는 단어를 중심으로 생각해보시면 됩니다.
예시코드를 살펴보시죠.
let apple: [String] = ["iPhone", "iPad", "Macbook","AirPods"]
apple.forEach { i in
print(i)
}
// ==== console
// iPhone
// iPad
// Macbook
// AirPods
(forEach의 경우 i in을 생략하고, print($0)로도 사용할 수 있습니다. 여기선 정확한 설명을 위해 상세구현합니다.)
역시 간단한 코드입니다. apple이란 배열을 forEach 함수에 반복시킨 결과를 나타내고 있습니다.
동일하게 이 코드를 천천히 해석해봅시다.
forEach 정의를 대입해서 다시 살펴보면,
"apple이란 배열(array)가 있을 때, 반복 실행하려는 코드(print)를 파라미터(forEach)로 받고,
이렇게 저장된 요소는 클로저 상수( i )로 전달되어 실행된다."
라고 해석해볼 수 있습니다.
즉, '반복하려는 내용을 묶어서 통째로 넘겨준다'라고 생각해볼 수도 있겠네요.
두 함수의 결과값은 동일하나 분명 '다릅니다'
for-in = 개발자가 '수동 구현'하는 '반복문'
forEach = 개발자가 클로저로 작성해서 넘겨주는 것. = '자동 구현'
수동 구현을 한다는 것은 개발자가 컨트롤할 수 있지만,
자동 구현을 한다면, 개발자가 일일이 컨트롤할 수 없습니다.
이 말을 코드에 대입해보면,
우리가 반복문을 쓸 때 대표적으로 사용하는 코드들이
break, continue, return 등이 있을텐데, 이 용어들이 대략 어떻게 사용될지 감이 오시나요?
for-in ( 수동 구현 )이라면, break, continue, return을 사용했을 때,
i란 루프 상수에 apple[0]의 값이 추출되고 나서
break(멈추거나)
continue(계속 시키거나)
return(끝내거나)
할 수 있겠죠?
// (1) break
func forinVSforEach1() {
let apple: [String] = ["iPhone", "iPad", "Macbook","AirPods"]
for i in apple {
print(i)
break
}
}
forinVSforEach1() // 실행
// ==== console
// iPhone
// ----
// (2) continue
func forinVSforEach2() {
let apple: [String] = ["iPhone", "iPad", "Macbook","AirPods"]
for i in apple {
print(i)
continue
}
}
forinVSforEach2() // 실행
// ==== console
// iPhone
// iPad
// Macbook
// AirPods
// ----
// (3) return
func forinVSforEach3() {
let apple: [String] = ["iPhone", "iPad", "Macbook","AirPods"]
for i in apple {
print(i)
return
}
}
forinVSforEach3() // 실행
// ==== console
// iPhone
하지만, forEach ( 자동 구현 )이라면?
클로저로 작성해서 자동구현하라고 통째로 값을 넘겨줬기 때문에,
break(멈춰야하는데, 멈출게 없어서 -> 불가능),
continue(계속 시켜야하는데, 시킬게 없어서 -> 불가능)
return(끝내야하는데, 자동구현이 다 끝난다음에 즉, 클로저 상수가 끝나고 나서 끝낼 수 있음.)
(즉 -> 가능은 하지만, 다음 클로저 상수가 없다면, 쓸모 X)
// (1) break
func forinVSforEach1() {
let apple: [String] = ["iPhone", "iPad", "Macbook","AirPods"]
apple.forEach { i in
print(i)
break // Error!
}
}
forinVSforEach1() // 실행
// ==== console
// Error!
// ----
// (2) continue
func forinVSforEach2() {
let apple: [String] = ["iPhone", "iPad", "Macbook","AirPods"]
apple.forEach { i in
print(i)
continue // Error!
}
}
forinVSforEach2() // 실행
// ==== console
// Error!
// ----
// (3) return
func forinVSforEach3() {
let apple: [String] = ["iPhone", "iPad", "Macbook","AirPods"]
apple.forEach { i in
print(i)
return
}
}
forinVSforEach3() // 실행
// ==== console
// iPhone
// iPad
// Macbook
// AirPods
이렇게 간단히 for - in과 forEach에 대해 알아보았습니다.!
헷갈리는 내용이였는데, 천천히 정리하니 생각보다 재밌고,
어떤 상황에서 쓰일지 기대되네요.
오류가 있거나, 피드백이 있다면 언제든지 댓글로 알려주세요!
감사합니다 :)