프로그래밍풀스택 개발자

Go에서 익명 함수는 어떻게 구현되고 사용되며, 함수 매개변수로 사용할 때의 특징은 무엇인가요?

Hintsage AI 어시스턴트로 면접 통과

답변.

질문의 역사

Go 언어에서 익명 함수(함수 리터럴, 클로저)는 함수형 스타일, 콜백, 알고리즘의 간결한 캡슐화를 지원하기 위해 등장했습니다. 이들은 컬렉션 처리, 비동기 작업 및 매개변수 전달에 자주 사용됩니다.

문제

익명 함수가 없다면 코드가 과도해집니다: 각 처리를 별도의 이름이 있는 함수로 가져가야 합니다. 그러나 이들로 인해 변수 "캡처" 작동 방식, 메모리 저장 위치, 인수로 전달할 때의 특성 등 여러 질문이 발생합니다. 변수를 외부에서 수정할 경우 캡처가 보호됩니까? 자주 발생하는 오류는 루프 내 변수 캡처를 잘못 진행하는 것입니다.

해결책

익명 함수는 리터럴로 선언되고 변수에 할당되거나 즉시 사용될 수 있습니다. 익명 함수가 외부 영역의 변수에 접근하면 이 변수는 "캡처"되어 클로저의 생명 주기 동안 유지됩니다. 함수의 매개변수로 익명 함수를 전달할 때는 일반적으로 시그니처와 호환되는 func 유형으로 전달됩니다. 대부분의 문제는 루프의 변수를 캡처할 때 발생하므로, 클로저로 로직을 감싸고자 한다면 반드시 루프 내에서 새로운 변수를 생성해야 합니다.

코드 예시:

func operate(nums []int, op func(int) int) []int { res := make([]int, len(nums)) for i, n := range nums { res[i] = op(n) } return res } func main() { arr := []int{1, 2, 3} out := operate(arr, func(x int) int {return x * x}) fmt.Println(out) // [1 4 9] }

주요 특징:

  • Go에서 어떤 func 리터럴도 외부에서 변수를 캡처할 수 있습니다.
  • 클로저는 이를 참조하거나 사용되는 동안 "살아있어" 원래 범위를 벗어난 후에도 계속 유지됩니다.
  • 익명 함수를 매개변수로 전달하는 것은 func 타입으로 간단하게 수행됩니다.

함정 질문.

익명 함수를 통해 변수가 변경 가능한 값으로 캡처될 때 무엇이 발생합니까?

모든 클로저는 동일한 변수를 캡처하며, 루프를 빠져나간 후 함수를 호출할 때 동일한 값을 얻게 됩니다.

코드 예시:

func main() { a := []func(){} for i := 0; i < 3; i++ { a = append(a, func() { fmt.Println(i) }) } for _, f := range a { f() } // 3 3 3 }

이를 피하려면 루프 본문에 새로운 변수를 생성하십시오:

for i := 0; i < 3; i++ { j := i a = append(a, func() { fmt.Println(j) }) // 0 1 2 }

익명 함수를 interface{} 타입의 값으로 사용할 수 있습니까?

함수는 interface{}와만 호환되며 다른 인터페이스와는 호환되지 않으며 서로 비교할 수 없습니다(니lt 제외). 클로저를 interface{}로 전달하면, func 시그니처로 형변환하여만 호출할 수 있습니다.

익명 함수가 재귀적일 수 있습니까?

네, 그러나 클로저에 대한 이름 변수를 먼저 선언한 후 그 함수 자신에게 할당해야 합니다.

코드 예시:

var fib func(n int) int fib = func(n int) int { if n < 2 { return n } return fib(n-1) + fib(n-2) } fmt.Println(fib(10)) // 55

전형적인 오류 및 안티 패턴

  • 추가적인 지역 변수를 사용하지 않고 루프 변수를 캡처하기
  • 명백한 필요 없이 많은 양의 힙에서 캡처된 객체와 함께 클로저를 전달하기
  • 가독성과 코드 재사용이 필요할 때 문맥 밖에서 익명 함수를 사용하는 것

실제 사례

부정적인 사례

콜백 목록을 순회하면서 개발자가 변수 반복자 캡처로서 핸들러를 클로저로 바인딩합니다. 모든 콜백이 일치하지 않는 값으로 작업하여 버그를 발생시킵니다.

장점:

  • 최소한의 템플릿 코드

단점:

  • 루프에서의 값 관련 일반적인 오류, 잡기 어려운 버그

긍정적인 사례

루프 내에서 각 클로저를 위한 새로운 변수를 생성하여 값의 정확한 캡처와 기대한 동작을 보장합니다.

장점:

  • 권장 사항을 따르기만 하면 버그를 피할 수 있습니다.
  • 코드는 간결하고 안전합니다.

단점:

  • 클로저 및 범위의 세부사항에 대한 지식이 필요합니다.