고차 함수
고차 함수는 함수를 매개 변수로 사용하거나 함수를 반환하는 함수입니다. 그러한 함수의 좋은 예는 lock()
잠금 객체와 함수 를 취하여 잠금을 획득하고, 함수를 실행하고 잠금을 해제하는 것입니다.
fun <T> lock(lock: Lock, body: () -> T): T {
lock.lock()
try {
return body()
}
finally {
lock.unlock()
}
}
의 위의 코드를 검사하자 : body
이 기능 유형을 : () -> T
그래서 매개 변수를 사용하지 않는 입력의 값을 반환하는 기능을해야하는데 T
. 그것은 try 블록 안에서 호출 되며, lock
에 의해 보호되고 그 결과는 lock()
함수에 의해 반환됩니다 .
우리가 호출하기를 원한다면 lock()
, 다른 함수를 인자로 전달할 수있다 ( 함수 참조를 보라 ) :
fun toBeSynchronized() = sharedResource.operation()
val result = lock(lock, ::toBeSynchronized)
또 다른, 더 편리한 방법은 람다 표현식 을 전달하는 것입니다 :
val result = lock(lock, { sharedResource.operation() })
람다 표현식에 대해서는 아래 에서 자세히 설명 하지만이 섹션을 계속 진행하기 위해 간단한 개요를 살펴 보겠습니다.
- 람다 표현식은 항상 중괄호로 둘러 쌉니다.
-
-> 앞에 파라미터를 선언합니다.
(매개 변수 유형은 생략 할 수 있음). -
-> 뒤에 몸체가 옵니다. (몸체가 존재 할때).
코틀린에서 함수에 대한 마지막 매개 변수가 함수이고 람다 식을 해당 인수로 전달하는 경우 괄호 밖에 지정할 수있는 규칙이 있습니다.
lock (lock) {
sharedResource.operation()
}
고차 함수의 또 다른 예는 다음과 map()
같습니다.
fun <T, R> List<T>.map(transform: (T) -> R): List<R> {
val result = arrayListOf<R>()
for (item in this)
result.add(transform(item))
return result
}
이 함수는 다음과 같이 호출 할 수 있습니다.
val doubled = ints.map { value -> value * 2 }
람다가 그 호출의 유일한 인수라면 호출의 괄호는 완전히 생략 될 수 있습니다.
it
: 단일 매개 변수의 암시 적 이름
또 다른 유용한 규칙은 함수 리터럴에 하나의 매개 변수 만있는 경우 그 선언을 생략 할 수 있으며 ->
그 이름은 다음과 it
같습니다.
ints.map { it * 2 }
이러한 규칙에 따라 LINQ 스타일 코드 를 작성할 수 있습니다.
strings.filter { it.length == 5 }.sortedBy { it }.map { it.toUpperCase() }
사용되지 않는 변수의 밑줄 (1.1 버전 이후)
lambda 매개 변수가 사용되지 않으면 이름 대신 밑줄을 사용할 수 있습니다.
map.forEach { _, value -> println("$value!") }
람다에서의 분리 선언 (1.1 버전 이후)
람다에서의 분리 선언은 분리 선언에서 설명합니다 .
인라인 함수
때로는 인라인 함수를 사용하여 고차 함수의 성능을 향상시키는 것이 좋습니다 .
람다 식과 익명 함수
람다 식 또는 익명 함수는 "함수 리터럴"입니다. 즉, 선언되지 않고 즉시 식으로 전달되는 함수입니다. 다음 예제를 고려하십시오.
max(strings, { a, b -> a.length < b.length })
함수 max
는 고차 함수입니다. 즉, 함수 값을 두 번째 인수로 취합니다. 이 두 번째 인수는 그 자체가 함수, 즉 함수 리터럴 인 표현식입니다. 함수로서 다음과 같습니다.
fun compare(a: String, b: String): Boolean = a.length < b.length
함수 유형
함수가 다른 함수를 매개 변수로 받아들이려면 해당 매개 변수에 대한 함수 유형을 지정해야합니다. 예를 들어 상기 기능 max
은 다음과 같이 정의됩니다.
fun <T> max(collection: Collection<T>, less: (T, T) -> Boolean): T? {
var max: T? = null
for (it in collection)
if (max == null || less(max, it))
max = it
return max
}
이 매개 변수 less
는 형식의 (T, T) -> Boolean
두 가지 매개 변수를 사용하고 첫 번째 매개 변수가 두 번째 매개 변수 보다 작 으면 true를 T
반환 하는 함수입니다 Boolean
.
본문에서 4 행 less
은 함수로 사용됩니다 T
. 두 개의 인수 유형을 전달하여 호출합니다 .
함수 유형은 위와 같이 작성되거나 각 매개 변수의 의미를 문서화하려는 경우 명명 된 매개 변수를 가질 수 있습니다.
val compare: (x: T, y: T) -> Int = ...
함수 유형의 Nullable 변수를 선언하려면 전체 함수 유형을 괄호로 묶고 뒤에 물음표를 넣으십시오.
var sum: ((Int, Int) -> Int)? = null
람다 식 구문
함수 형식의 리터럴 인 람다 식의 전체 구문 형식은 다음과 같습니다.
val sum = { x: Int, y: Int -> x + y }
람다 식은 항상 중괄호로 둘러 쌉니다. 전체 구문 형식의 매개 변수 선언은 중괄호 안에 들어가고 선택적 형식 주석을 사용하면 본문이 ->
부호 뒤에옵니다 . 유추 된 람다 반환 유형이 아닌 Unit
경우 람다 본문의 마지막 식 (또는 단일 식일 수도 있음)이 반환 값으로 처리됩니다.
모든 선택적인 주석을 남겨두면 남은 것은 다음과 같습니다.
val sum: (Int, Int) -> Int = { x, y -> x + y }
람다 표현식에는 하나의 매개 변수 만있는 것이 일반적입니다. Kotlin이 자체적으로 서명을 알아낼 수 있다면 우리는 유일한 매개 변수를 선언 할 수 없으며 암시 적으로 다음과 같이 이름으로 선언합니다 it
.
ints.filter { it > 0 } // this literal is of type '(it: Int) -> Boolean'
우리는 명시 적으로 사용 람다에서 값을 반환 할 수 있습니다 자격을 갖춘 리턴 구문을. 그렇지 않으면 마지막 표현식의 값이 내재적으로 리턴됩니다. 따라서 다음 두 스 니펫은 동일합니다.
ints.filter {
val shouldFilter = it > 0
shouldFilter
}
ints.filter {
val shouldFilter = it > 0
return@filter shouldFilter
}
함수가 다른 매개 변수를 마지막 매개 변수로 사용하면 괄호 안의 인수 목록 밖에서 람다 식 인수를 전달할 수 있습니다. callSuffix 의 문법을 참조하십시오 .
익명 함수
위에 제시된 람다 식 구문에서 빠진 한 가지는 함수의 반환 형식을 지정하는 기능입니다. 대부분의 경우 반환 유형을 자동으로 유추 할 수 있으므로 불필요합니다. 그러나 명시 적으로 지정해야하는 경우 익명의 함수 인 대체 구문을 사용할 수 있습니다 .
fun(x: Int, y: Int): Int = x + y
익명 함수는 이름이 생략된다는 점을 제외하고는 일반 함수 선언과 매우 흡사합니다. 그 몸체는 위의 그림과 같은 표현식이거나 블록 일 수 있습니다.
fun(x: Int, y: Int): Int {
return x + y
}
매개 변수 및 반환 유형은 정규 함수와 동일한 방식으로 지정됩니다. 단, 매개 변수 유형을 컨텍스트에서 유추 할 수있는 경우 생략 할 수 있습니다.
ints.filter(fun(item) = item > 0)
익명 함수에 대한 반환 유형 유추는 일반 함수처럼 작동합니다. 반환 유형은 표현 본문이있는 익명 함수에 대해 자동으로 유추 Unit
되며 블록 본문이있는 익명 함수에 대해 명시 적으로 지정되어야합니다 .
익명 함수 매개 변수는 항상 괄호 안에 전달됩니다. 괄호 밖에서 함수를 그대로 두는 것을 허용하는 속기 구문은 람다 식에만 적용됩니다.
람다 식과 익명 함수의 또 다른 차이점은 비 지역 반환 의 동작입니다 . 반환 레이블이없는 문은 항상 선언 함수에서 반환 재미 키워드. 이것은 것을 의미 복귀 반면 람다 식 내부가 바깥 쪽 함수로부터 반환 창 익명 함수 내부 익명 함수 자체에서 복귀한다.
클로저
람다 표현식이나 익명의 함수 ( 로컬 함수 와 객체 표현식은 물론 )는 클로저 , 즉 외부 범위에서 선언 된 변수에 액세스 할 수 있습니다 . Java와 달리 클로저에서 캡처 된 변수는 수정할 수 있습니다.
var sum = 0
ints.filter { it > 0 }.forEach {
sum += it
}
print(sum)
리시버가있는 함수 리터럴
Kotlin은 지정된 리시버 오브젝트 로 함수 리터럴을 호출하는 기능을 제공 합니다 . 함수 리터럴의 본문에서는 추가 한정자없이 해당 리시버 개체의 메서드를 호출 할 수 있습니다. 이는 함수의 본문 내부에있는 리시버 오브젝트의 멤버에 액세스 할 수있는 확장 함수와 유사합니다. 사용법의 가장 중요한 예 중 하나는 타입 안전 Groovy 스타일 빌더 입니다.
이러한 함수 리터럴 유형은 리시버가있는 함수 유형입니다.
sum : Int.(other: Int) -> Int
함수 리터럴은 마치 리시버 객체의 메소드 인 것처럼 호출 할 수 있습니다.
1.sum(2)
익명 함수 구문을 사용하면 함수 리터럴의 수신기 유형을 직접 지정할 수 있습니다. 이것은 수신기로 함수 유형의 변수를 선언하고 나중에 사용해야하는 경우 유용 할 수 있습니다.
val sum = fun Int.(other: Int): Int = this + other
수신자 유형이 컨텍스트에서 유추 될 수있을 때 람다 식은 수신자와 함께 함수 리터럴로 사용될 수 있습니다.
class HTML {
fun body() { ... }
}
fun html(init: HTML.() -> Unit): HTML {
val html = HTML() // 리시버 객체 생성
html.init() // 람다에 리시버 객체를 전달
return html
}
html { // 리시버를 가진 람다 시작
body() // 리시버 객체의 메서드 호출
}
'프로그래밍 > kotlin' 카테고리의 다른 글
코틀린 - 확장 (0) | 2021.04.25 |
---|---|
코틀린 - 함수 (0) | 2021.04.18 |
코틀린 - 인라인 함수 (0) | 2021.04.18 |
코틀린 - 코루틴 (0) | 2021.04.18 |
코틀린 - 분리 선언 (0) | 2021.04.18 |