일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 옵저버 패턴
- Functional Programming
- ㅋㅁ
- 코틀린
- 팩토리 메소드
- builderPattern
- 빌터패턴
- r
- 추상팩토리패턴
- 프로토타입 패턴
- designPattern
- 싱글톤
- 함수형프로그래밍
- Observer Pattern
- Kotlin
- PrototypePattern
- a
- Design Pattern
- F
- 디자인패턴
- Singleton
- 추상 팩토리
- factory method
- Abstract Factory
- ㅓ
- El
- 디자인패턴 #
- Today
- Total
오늘도 더 나은 코드를 작성하였습니까?
고차 함수 : 파라미터와 반환 값으로 람다 사용하기. 본문
고차함수의 정의
- 다른 함수를 인자로 받거나 함수를 반환하는 함수.
함수타입
val sum = {x:Int, y: Int -> x+y}
val action = {println(14)}
val sum: (Int, Int) -> Int = {x:Int, y: Int -> x+y}
val action: () -> Unit = {println(14)}
*Unit은 값을 반환 하지 않는 함수의 반환타입인데, 일반적인 값을 반환하지 않는 함수는 정의할 때는 생략이 가능하지만, 함수타입을 선언 할때는 생략을 해서는 안된다.
* 괄호에 주의하며 아래코드를 보면된다
// 반환값이 널값이될 수 있다.
val canReturnNull: (Int, Int) -> Int? = {x , y -> null}
// 함수자체가 널이될 수 있다.
val funOrNull: ((Int, Int) -> Int)? = null
.*함수타입에서 파라미터 이름을 지정가능하다
fun performRequest(url: String, callback: (code: Int, content: String) -> Unit){
/*
...
* */
}
fun main() {
val url = "http://kotlin.com"
performRequest(url){code, content -> /* .... */ }
}
filter 확장함수 구현하기
fun String.filter(predicate: (Char) -> Boolean): String {
val sb = StringBuilder()
for(index in 0 until length){
val element = get(index)
if(predicate(element)) sb.append(element)
}
return sb.toString()
}
fun main() {
println("abc12dceg24".filter { it in 'a'..'z' })
}
자바에서 함수타입 사용하기
함수타입은 컴파일 코드 안에서 일반 인터페이스로 변화한다. 즉, 함수타입 변수는 FunctionN 인터페이스를 구현한 객체를 저장하는것,
인자의 개수에 따라 Function0<R>(인자 없음), Function2<P1, R>(인자 1개) 등의 인터페이스를 제공한다.
각, 인터페이스에는 invoke 메소드 정의가 하나들어 있다. invoke 함수를 호출하면 함수를 실행할수있다.
함수타입변수는 FunctionN 인터페이스를 구현하는 클래스 인스턴스를 저장하여 invoke 메소드 본문에 람다 본문이 들어간다.
코틀린
fun processTheAnswer(f:(Int) -> Int) {
println(f(42))
}
자바8 이후
processTheAnswer(number -> number+1)
자바8 이전
processAnswer(
new Function1<Integer, Integer>(){
@Overide
public Integer invoke(Integer number){
System.out.println(number);
return number + 1;
}
}
);
자바에서 코틀린 표준라이브러리가 제공하는 람다를 인자로 받는 확장함수를 호출할수 있지만, 수신객체를 첫번째 인자로 반드시 명시적으로 넘겨야 하기 때문에 코드가 깔끔하지 못하다.
List<String> strings = new ArrayList();
strings.add("42");
CollectionsKt.forEach(strings, s -> { // 수신객체 인자로 넘기기.
System.out.println(s);
return Unit.INSTANCE; // 코틀린의 Unit타입을 명시적 반환
})
디폴트 값을 지정한 함수타입 파라미터
fun <T> Collection<T>.jointToString(
separator: String = ",",
prefix: String = "",
postfix: String = "",
transform: (T) -> String = { it -> it.toString() }
): String {
val result = StringBuilder(prefix)
for ((index, element) in withIndex()) {
if (index > 0) result.append(separator)
result.append(transform(element))
}
result.append(postfix)
return result.toString()
}
fun main() {
val letters = listOf("a", "B", "c", "D")
println(letters.jointToString { it.toUpperCase() })
println(letters.jointToString { it.toLowerCase() })
println(letters.jointToString("/"))
}
StringBuilder.append( o: Any? ) 이 함수를 내부적으로 이용하는데 항상 toString을 사용하여 문자열을 변환한다.
대부분의 경우 잘 작동하지만, toString 아닌 다른 원소를 바꿔서 나누고 싶은 경우는,
그러면 {원소 -> 문자열} 하는 람다를 joinToString으로 넘겨주면 되는데 매번 넘기기보단 default 값의 람다를 인자로 만든다.
널이될 수 있는 함수 타입 파라미터
- 널이 될수 있는 함수를 파라미터로 받는다면 호출이 불가능할 수 있다 --> NPE
- null 검사를 명시적으로 해주거나
- ?. 안전호출을 이용하자
fun <T> Collection<T>.jointToString(
separator: String = ",",
prefix: String = "",
postfix: String = "",
transform: ((T) -> String)? = null
): String {
val result = StringBuilder(prefix)
for ((index, element) in withIndex()) {
if (index > 0) result.append(separator)
val str = transform?.invoke(element)
?: element.toString()
result.append(str)
}
result.append(postfix)
return result.toString()
}
함수에서 함수를 반환하기
배송타입에 따라 주문의 배송비를 계산해주는 함수를 반환하는 함수
enum class Delivery {STANDARD, EXPEDITED}
class Order(val itemCount: Int)
fun getShippingCostCalculator(delivery: Delivery): (Order) -> Double{
if (delivery == Delivery.EXPEDITED){
return {order -> 6 + 2.1 * order.itemCount }
}
return {order -> 1.2 * order.itemCount }
}
사용자가 입력 창에 입력한 문자열과 매치되는 사람의 연락처만 화면에 표시하되, 연락처 정보가 있는 사람과 없는 것을 구별하기
data class Person(
val firstName: String,
val lastName: String,
val phoneNumber: String?
)
class ContactListFilters {
var prefix: String = ""
var onlyWithPhoneNumber: Boolean = false
fun getPredicate(): (Person) -> Boolean {
val startsWithPrefix = { p: Person ->
p.firstName.startsWith(prefix) || p.lastName.startsWith(prefix)
}
// 전화번호 정보가 없어도 연락처 표시
if (!onlyWithPhoneNumber) {
return startsWithPrefix
}
// 전화번호 정보가 있어야만 표시
return { startsWithPrefix(it) && it.phoneNumber != null }
}
}
fun main() {
val contacts = listOf(
Person("Dmitry", "Jemerov", "123-4567"),
Person("Svetlana", "Isacova", null)
)
val contactListFilters = ContactListFilters()
with(contactListFilters){
prefix = "Sv"
onlyWithPhoneNumber = false
}
println(contacts.filter( contactListFilters.getPredicate() ))
}
람다를 활용한 중복 제거
각 사이트의 페이지 마다 사용자가 있던 시간과 접속한 OS 기록을 분석하기.
data class SiteVisit(
val path: String,
val duration: Double,
val os: OS
)
enum class OS { WINDOWS, LINUX, MAC, IOS, ANDROID }
val log = listOf(
SiteVisit("/", 34.0, OS.WINDOWS),
SiteVisit("/", 22.0, OS.MAC),
SiteVisit("/login", 12.0, OS.WINDOWS),
SiteVisit("/signup", 8.0, OS.IOS),
SiteVisit("/", 16.3,OS.ANDROID)
)
fun main() {
val averageWindowsDuration = log
.filter { it.os == OS.WINDOWS }
.map (SiteVisit::duration)
.average()
println(averageWindowsDuration)
}
일반 확장함수를 생성해 중복을 제거하기.
fun List<SiteVisit>.averageDuration(os: OS) =
filter { it.os == os }
.map { it.duration }
.average()
모바일 디바이스 사용자의 평균방문시간 구하기(하드 코딩)
fun main() {
val averageMobileDuration = log.filter { it.os in setOf(OS.IOS, OS.ANDROID) }
.map { it.duration }
.average()
println(averageMobileDuration)
}
고차함수를 이용하여 중복을 줄여보기.
fun List<SiteVisit>.averageDurationFor(predicate: (SiteVisit) -> Boolean) =
filter(predicate)
.map(SiteVisit::duration)
.average()
fun main() {
println(log.averageDurationFor { it.os in setOf(OS.ANDROID, OS.IOS) })
println(log.averageDurationFor { it.os == OS.IOS && it.path == "/signup"})
}
코드의 중복을 줄여주는데 함수타입과 람다는 매우 도움을 준다.
코드의 일부분을 복사 붙혀넣기 하기전에 한 번 람다를 생성해 고차함수를 이용한다면 코드의 중복을 줄일 수 있다.
'Kotlin in Action > 코틀린 답게 사용하기' 카테고리의 다른 글
컬렉션 연산 inline (0) | 2021.02.25 |
---|---|
인라인 함수: 람다의 부가 비용 없애기 (0) | 2021.02.25 |
연산자 오버로딩과 기타 관례(위임 프로퍼티 기타) (0) | 2020.08.25 |
연산자 오버로딩과 기타 관례(위임 프로퍼티 기타) (0) | 2020.08.25 |
연산자 오버로딩과 기타 관례(프로퍼티 접근자 로직 재활용: 위임 프로퍼티) (0) | 2020.08.25 |