20. • Pure Function
FP?
fun main(args: Array<String>) {
println(pureFunction("FP")) // Hello FP
}
fun pureFunction(input: String): String {
return "Hello " + input
}
21. • Pure Function
FP?
fun main(args: Array<String>) {
println(pureFunction("FP")) // Hello FP
println(pureFunction("FP")) // Hello FP
println(pureFunction("FP")) // Hello FP
}
fun pureFunction(input: String): String {
return "Hello " + input
}
22. • Pure Function
FP?
fun main(args: Array<String>) {
println(nonPureFunction("FP")) // Hello FP
}
val strBuilder: StringBuilder = StringBuilder("Hello ")
fun nonPureFunction(input: String): String {
return strBuilder.append(input).toString()
}
23. • Pure Function
FP?
fun main(args: Array<String>) {
println(nonPureFunction("FP")) // Hello FP
}
val strBuilder: StringBuilder = StringBuilder("Hello ")
fun nonPureFunction(input: String): String {
return strBuilder.append(input).toString()
}
24. • Pure Function
FP?
fun main(args: Array<String>) {
println(nonPureFunction("FP")) // Hello FP
println(nonPureFunction("FP")) // Hello FPFP
println(nonPureFunction("FP")) // Hello FPFPFP
}
val strBuilder: StringBuilder = StringBuilder("Hello ")
fun nonPureFunction(input: String): String {
return strBuilder.append(input).toString()
}
25. • Pure Function
FP?
fun main(args: Array<String>) {
println(nonPureFunction2(4)) // 5
}
var x = 1
fun nonPureFunction2(input: Int): Int{
return input + x
}
26. • Pure Function
FP?
fun main(args: Array<String>) {
println(nonPureFunction2(4)) // 5
}
var x = 1
fun nonPureFunction2(input: Int): Int{
return input + x
}
27. • Pure Function
FP?
fun main(args: Array<String>) {
println(nonPureFunction2(4)) // 5
println(nonPureFunction2(4)) // 5
println(nonPureFunction2(4)) // 5
}
var x = 1
fun nonPureFunction2(input: Int): Int{
return input + x
}
28. • Pure Function
FP?
fun main(args: Array<String>) {
println(nonPureFunction2(4)) // 5
println(nonPureFunction2(4)) // 5
println(nonPureFunction2(4)) // 5
x = 10
}
var x = 1
fun nonPureFunction2(input: Int): Int{
return input + x
}
29. • Pure Function
FP?
fun main(args: Array<String>) {
println(nonPureFunction2(4)) // 5
println(nonPureFunction2(4)) // 5
println(nonPureFunction2(4)) // 5
x = 10
println(nonPureFunction2(4)) // 14
}
var x = 1
fun nonPureFunction2(input: Int): Int{
return input + x
}
31. • Lazy evaluation
FP?
val lazyValue: String by lazy {
println("시간이 오래 걸리는 작업")
"hello"
}
fun main(args: Array<String>) {
println(lazyValue)
println(lazyValue)
}
32. • Lazy evaluation
FP?
val lazyValue: String by lazy {
println("시간이 오래 걸리는 작업")
"hello"
}
fun main(args: Array<String>) {
println(lazyValue)
println(lazyValue)
}
시간이 오래 걸리는 작업
hello
hello
33. • Lazy evaluation
FP?
val lazyValue: String by lazy {
println("시간이 오래 걸리는 작업")
"hello"
}
fun main(args: Array<String>) {
println(lazyValue)
println(lazyValue)
}
시간이 오래 걸리는 작업
hello
hello
40. Functional Data Structure
fun main(args: Array<String>) {
val value = 3
val result = plusThree(value)
}
fun plusThree(x: Int): Int {
return x + 3
}
41. Functional Data Structure
fun main(args: Array<String>) {
val value = 3
val result = plusThree(value)
println(result) // 6
println(value) // 3
}
fun plusThree(x: Int): Int {
return x + 3
}
42. Functional Data Structure
fun main(args: Array<String>) {
val list = listOf(1, 2)
val listResult = plusThree(list)
}
fun plusThree(list: List<Int>): List<Int> {
return list.plus(3)
}
43. Functional Data Structure
fun main(args: Array<String>) {
val list = listOf(1, 2)
val listResult = plusThree(list)
println(list) // [1, 2]
println(listResult) // [1, 2, 3]
}
fun plusThree(list: List<Int>): List<Int> {
return list.plus(3)
}
47. Functional Data Structure
• Data Share
val list = listOf(1, 2)
val listResult = plusThree(list)
1 2
Cons Cons
Head Tail Head Tail
list
NIL
48. Functional Data Structure
• Data Share
fun plusThree(list: List<Int>): List<Int> {
return list.plus(3)
}
Tail NIL1 2
Head Tail Head Tail
NIL
list
49. Functional Data Structure
• Data Share
list
fun plusThree(list: List<Int>): List<Int> {
return list.plus(3)
}
Tail 3
Cons
Head Tail
NIL1 2
Head Tail Head Tail
1 2
Head Tail Head Tail
NIL
list
• Data Share
50. Functional Data Structure
• Data Share
1 2
Cons Cons
Head Tail Head Tail
3
Cons
Head Tail
NIL
val listResult = plusThree(list)
listResult
70. Recursion
• Int 리스트 값중 최대값을 구하는 함수 getMaxValue 함수를 재귀
로 구현하자
fun getMaxValue(list: List<Int>): Int = when {
list.isEmpty() -> throw NullPointerException()
list.size == 1 -> list.first()
}
종료조건?
71. Recursion
• Int 리스트 값중 최대값을 구하는 함수 getMaxValue 함수를 재귀
로 구현하자
fun getMaxValue(list: List<Int>): Int = when {
list.isEmpty() -> throw NullPointerException()
list.size == 1 -> list.first()
}
종료 조건으로 수렴?
72. Recursion
• Int 리스트 값중 최대값을 구하는 함수 getMaxValue 함수를 재귀
로 구현하자
fun getMaxValue(list: List<Int>): Int = when {
list.isEmpty() -> throw NullPointerException()
list.size == 1 -> list.first()
else -> if (list.first() > getMaxValue(list.drop(1))) {
list.first()
} else {
getMaxValue(list.drop(1))
}
}
종료 조건으로 수렴?
90. Function Type
val functionType: () -> Unit
val functionType1: (Int) -> Unit
val functionType2: (Int, String) -> Unit
functionType = { println("FP") }
functionType1 = { value -> println(value.toString()) }
functionType2 = { intValue, stringValie ->
println("intValue $intValue, stringValue $stringValie")
}
91. Function Type
val functionType: () -> Unit
val functionType1: (Int) -> Unit
val functionType2: (Int, String) -> Unit
functionType = { println("FP") }
functionType1 = { value -> println(value.toString()) }
functionType2 = { intValue, stringValie ->
println("intValue $intValue, stringValue $stringValie")
}
functionType() // FP
functionType1(10) // 10
functionType2(10, "FP") // intValue 10, stringVAlue FP
92. • 연습문제 2:
• Int 타입 2개를 입력받아서, 입력받은 두 값을 곱한 결과 값 을
반환하는 함수타입 프로퍼티 product를 만들어 보자.
Function Type
93. • 연습문제 2:
• Int 타입 2개를 입력받아서, 입력받은 두 값을 곱한 결과 값 을
반환하는 함수타입 프로퍼티 product를 만들어 보자.
• 연습문제 2:
• String 타입 3개를 입력받아서, 하나의 스트링으로 합쳐주는 함
수타입 프로퍼티 appendStrings 를 만들어보자.
Function Type
95. • Pass a function as an Argument
• Return a function
• Assign a function to a data structure
First Class Citizen
96. • Pass a function as an Argument
First Class Citizen
fun function(param: () -> Unit) {
// something
}
97. • Return a function
First Class Citizen
fun function2(): () -> Unit {
return { println("FP") }
}
98. • Assign a function to a data structure
First Class Citizen
val value1: () -> Unit = { println("FP") }
99. • Assign a function to a data structure
First Class Citizen
val value1: () -> Unit = { println("FP") }
val value2: List<() -> Unit> = listOf({ println("FP") })
100. • Assign a function to a data structure
First Class Citizen
val value1: () -> Unit = { println("FP") }
val value2: List<() -> Unit> = listOf({ println("FP") })
val value3: Map<String, () -> Unit> =
mapOf("FP" to { println("FP") })
110. Higher-order function
• Pass a function as an Argument OR Return a function
fun higherOrderFunction1(func: () -> Unit) {
func()
}
fun higherOrderFunction2(): () -> Unit {
return { println("FP") }
}
fun higherOrderFunction3(func: () -> Unit): () -> Unit {
return func
}
127. CallByName
• 함수의 값을 전달하지 않고 함수의 이름을 전달 할수 있다.
• 함수의 이름을 전달하면, 전달하는 시점에 함수가 실행되지 않는다.
128. CallByName
• 함수의 값을 전달하지 않고 함수의 이름을 전달 할수 있다.
• 함수의 이름을 전달하면, 전달하는 시점에 함수가 실행되지 않는다.
• 특정 상황에 따라 크고 무거운 연산을 하거나 하지 않을 경우 이름
에 의한 호출을 하면 훨씬 효율적이다.
129. CallByName
• 함수의 값을 전달하지 않고 함수의 이름을 전달 할수 있다.
• 함수의 이름을 전달하면, 전달하는 시점에 함수가 실행되지 않는다.
• 특정 상황에 따라 크고 무거운 연산을 하거나 하지 않을 경우 이름
에 의한 호출을 하면 훨씬 효율적이다.
게으른 연산(Lazy Evaluation)
132. Partial Applied Function
• Partial Applied Function
val func: (Int, Int) -> Int = { x, y -> x + y }
println(func(5, 10)) // 15
133. Partial Applied Function
• Partial Applied Function
val func: (Int, Int) -> Int = { x, y -> x + y }
println(func(5, 10)) // 15
x 값은 알고있지만 y는 나중에 알수 있다면??
134. Partial Applied Function
• Partial Applied Function
x 값은 알고있지만 y는 나중에 알수 있다면??
fun <P1, P2, R> ((P1, P2) -> R).partialAppliedFunction(x: P1): (P2) -> R {
return { p2 -> this(x, p2) }
}
135. Partial Applied Function
• Partial Applied Function
x 값은 알고있지만 y는 나중에 알수 있다면??
fun <P1, P2, R> ((P1, P2) -> R).partialAppliedFunction(x: P1): (P2) -> R {
return { p2 -> this(x, p2) }
}
136. Partial Applied Function
• Partial Applied Function
x 값은 알고있지만 y는 나중에 알수 있다면??
fun <P1, P2, R> ((P1, P2) -> R).partialAppliedFunction(x: P1): (P2) -> R {
return { p2 -> this(x, p2) }
}
137. Partial Applied Function
• Partial Applied Function
x 값은 알고있지만 y는 나중에 알수 있다면??
fun <P1, P2, R> ((P1, P2) -> R).partialAppliedFunction(x: P1): (P2) -> R {
return { p2 -> this(x, p2) }
}
138. Partial Applied Function
• Partial Applied Function
x 값은 알고있지만 y는 나중에 알수 있다면??
val partial = func.partialAppliedFunction(5)
// doSomething
139. Partial Applied Function
• Partial Applied Function
val partial = func.partialAppliedFunction(5)
// doSomething
println(partial(10)) // 15
140. Partial Applied Function
• Partial Applied Function
val multiThree = { x: Int, y: Int, z: Int -> x * y * z }
println(multiThree(1, 2, 3)) // 6
141. Partial Applied Function
• Partial Applied Function
x 값은 알고있지만 y, z 는 나중에 알수 있다면??
val multiThree = { x: Int, y: Int, z: Int -> x * y * z }
println(multiThree(1, 2, 3)) // 6
142. Partial Applied Function
• Partial Applied Function
x 값은 알고있지만 y, z 는 나중에 알수 있다면??
fun <P1, P2, P3, R> ((P1, P2, P3) -> R).partialAppliedFunction(x: P1): (P2, P3) -> R {
return { p2, P3 -> this(x, p2, P3) }
}
143. Partial Applied Function
• Partial Applied Function
x 값은 알고있지만 y, z 는 나중에 알수 있다면??
fun <P1, P2, P3, R> ((P1, P2, P3) -> R).partialAppliedFunction(x: P1): (P2, P3) -> R {
return { p2, P3 -> this(x, p2, P3) }
}
144. Partial Applied Function
• Partial Applied Function
x 값은 알고있지만 y, z 는 나중에 알수 있다면??
fun <P1, P2, P3, R> ((P1, P2, P3) -> R).partialAppliedFunction(x: P1): (P2, P3) -> R {
return { p2, P3 -> this(x, p2, P3) }
}
145. Partial Applied Function
• Partial Applied Function
x 값은 알고있지만 y, z 는 나중에 알수 있다면??
fun <P1, P2, P3, R> ((P1, P2, P3) -> R).partialAppliedFunction(x: P1): (P2, P3) -> R {
return { p2, P3 -> this(x, p2, P3) }
}
146. Partial Applied Function
• Partial Applied Function
x 값은 알고있지만 y, z 는 나중에 알수 있다면??
fun <P1, P2, P3, R> ((P1, P2, P3) -> R).partialAppliedFunction(x: P1): (P2, P3) -> R {
return { p2, P3 -> this(x, p2, P3) }
}
147. Partial Applied Function
• Partial Applied Function
x 값은 알고있지만 y, z 는 나중에 알수 있다면??
val partial2 = multiThree.partialAppliedFunction(1)
println(partial2(2, 3)) // 6
148. • 연습문제 3:
• 입력값 4개를 중 2개를 먼저 입력받고 나머지 2개는 나중에 받
는 (P1, P2, P3, P4) -> R의 확장함수 면서 부분 함수인
partialAppliedFunction을 만들자.
• Int값 4개를 입력받아서 모든 값을 더한 결과를 반환하는 함수타
입 프로퍼티 sumFour 를 만들자.
• sumFor에 partialAppliedFunction을 적용해보자.
Partial Applied Function
150. Partial Applied Function
• Curring
val multiThree = { x: Int, y: Int, z: Int -> x * y * z }
println(multiThree(1, 2, 3)) // 6
x 값은 컴파일 타임에 알고 있지만 y, z값은 런타임에 각각 전달받는다면?
151. Partial Applied Function
• Curring
fun <P1, P2, P3, R> ((P1, P2, P3) -> R).curried(): (P1) -> (P2) -> (P3) -> R =
{ p1: P1 -> { p2: P2 -> { p3: P3 -> this(p1, p2, p3) } } }
x 값은 컴파일 타임에 알고 있지만 y, z값은 런타임에 각각 전달받는다면?
152. Partial Applied Function
• Curring
fun <P1, P2, P3, R> ((P1, P2, P3) -> R).curried(): (P1) -> (P2) -> (P3) -> R =
{ p1: P1 -> { p2: P2 -> { p3: P3 -> this(p1, p2, p3) } } }
val curriedMultiThree = multiThree.curried()
println(curriedMultiThree(1)(2)(3)) // 6
x 값은 컴파일 타임에 알고 있지만 y, z값은 런타임에 각각 전달받는다면?
153. Partial Applied Function
• Curring
fun <P1, P2, P3, R> ((P1, P2, P3) -> R).curried(): (P1) -> (P2) -> (P3) -> R =
{ p1: P1 -> { p2: P2 -> { p3: P3 -> this(p1, p2, p3) } } }
val curriedMultiThree = multiThree.curried()
println(curriedMultiThree(1)(2)(3)) // 6
val p1 = curriedMultiThree(1)
// doSomething
x 값은 컴파일 타임에 알고 있지만 y, z값은 런타임에 각각 전달받는다면?
154. Partial Applied Function
• Curring
fun <P1, P2, P3, R> ((P1, P2, P3) -> R).curried(): (P1) -> (P2) -> (P3) -> R =
{ p1: P1 -> { p2: P2 -> { p3: P3 -> this(p1, p2, p3) } } }
val curriedMultiThree = multiThree.curried()
println(curriedMultiThree(1)(2)(3)) // 6
val p1 = curriedMultiThree(1)
// doSomething
val p2 = p1(2)
// doSomething
x 값은 컴파일 타임에 알고 있지만 y, z값은 런타임에 각각 전달받는다면?
155. Partial Applied Function
• Curring
fun <P1, P2, P3, R> ((P1, P2, P3) -> R).curried(): (P1) -> (P2) -> (P3) -> R =
{ p1: P1 -> { p2: P2 -> { p3: P3 -> this(p1, p2, p3) } } }
val curriedMultiThree = multiThree.curried()
println(curriedMultiThree(1)(2)(3)) // 6
val p1 = curriedMultiThree(1)
// doSomething
val p2 = p1(2)
// doSomething
val p3 = p2(3)
// doSomething
println(p3) //6
x 값은 컴파일 타임에 알고 있지만 y, z값은 런타임에 각각 전달받는다면?
164. Collection(Stream)
• drop
public fun <T> Iterable<T>.drop(n: Int): List<T> {
require(n >= 0) { "Requested element count $n is less than zero." }
if (n == 0) return toList()
val list: ArrayList<T>
if (this is Collection<*>) {
val resultSize = size - n
if (resultSize <= 0)
return emptyList()
if (resultSize == 1)
return listOf(last())
list = ArrayList<T>(resultSize)
if (this is List<T>) {
if (this is RandomAccess) {
for (index in n until size)
list.add(this[index])
} else {
for (item in listIterator(n))
list.add(item)
}
return list
}
}
else {
list = ArrayList<T>()
}
var count = 0
for (item in this) {
if (count++ >= n) list.add(item)
}
return list.optimizeReadOnlyList()
}
167. Collection(Stream)
• dropWhile
public inline fun <T> Iterable<T>.dropWhile(predicate: (T) -> Boolean): List<T> {
var yielding = false
val list = ArrayList<T>()
for (item in this)
if (yielding)
list.add(item)
else if (!predicate(item)) {
list.add(item)
yielding = true
}
return list
}
171. Collection(Stream)
• filter vs dropWhile
val list = listOf(1, 2, 3, 4, 5, 6)
println(list.filter { value -> value % 2 == 1 })
println(list.dropWhile { value -> value % 2 == 1 })
172. Collection(Stream)
• filter vs dropWhile
val list = listOf(1, 2, 3, 4, 5, 6)
println(list.filter { value -> value % 2 == 1 })
// [1, 3, 5]
println(list.dropWhile { value -> value % 2 == 1 })
// [2, 3, 4, 5, 6]
173. Collection(Stream)
• take
public fun <T> Iterable<T>.take(n: Int): List<T> {
require(n >= 0) { "Requested element count $n is less than zero." }
if (n == 0) return emptyList()
if (this is Collection<T>) {
if (n >= size) return toList()
if (n == 1) return listOf(first())
}
var count = 0
val list = ArrayList<T>(n)
for (item in this) {
if (count++ == n)
break
list.add(item)
}
return list.optimizeReadOnlyList()
}
185. Collection(Stream)
• foldLeft vs foldRight
list1 = listOf("a", "b", "c", "d", "e", "f")
println(list1.foldRight("") { value, acc -> acc + value })
println(list1.fold("") { acc, value -> acc + value })
186. Collection(Stream)
• foldLeft vs foldRight
list1 = listOf("a", "b", "c", "d", "e", "f")
println(list1.foldRight("") { value, acc -> acc + value })
println(list1.fold("") { acc, value -> acc + value })
// abcdef
187. Collection(Stream)
• foldLeft vs foldRight
list1 = listOf("a", "b", "c", "d", "e", "f")
println(list1.foldRight("") { value, acc -> acc + value })
// fedcba
println(list1.fold("") { acc, value -> acc + value })
// abcdef
188. 연습문제
• exercise4 파일에 있는 list와 collection api를 사용해서 새로운
list를 만들자.
• 나이가 30 이상이고, single 인 사람들의 이름을 반환
189. 연습문제
• exercise4 파일에 있는 list와 collection api를 사용해서 새로운
list를 만들자.
• 나이가 30 이상이고, single 인 사람들의 이름을 반환
list.filter { person -> person.age > 30 }
.filter { person -> person.single }
.map { person -> person.name }
192. Algebraic Data Type
interface RGBInterface
class Red : RGBInterface
class Green : RGBInterface
class Blue : RGBInterface
data class Dog(val name: String, val age: Int)
enum class RGB {
RED, GREEN, BLUE
}
sealed class Option<out T>
data class Just<T>(val value: T) : Option<T>()
object None : Option<Nothing>()
193. Algebraic Data Type
• ProductType
interface RGBInterface
class Red : RGBInterface
class Green : RGBInterface
class Blue : RGBInterface
data class Dog(val name: String, val age: Int)
194. Algebraic Data Type
• ProductType
interface RGBInterface
class Red : RGBInterface
class Green : RGBInterface
class Blue : RGBInterface
195. Algebraic Data Type
• ProductType
interface RGBInterface
class Red : RGBInterface
class Green : RGBInterface
class Blue : RGBInterface
. . .
. . .
… : RGBInterface
… : RGBInterface
199. Algebraic Data Type
• SumType
enum class RGB {
RED, GREEN, BLUE
}
RED or GREEN or BLUE
200. Algebraic Data Type
• SumType
sealed class Option<out T>
data class Just<T>(val value: T) : Option<T>()
object None : Option<Nothing>()
201. Algebraic Data Type
• SumType
sealed class Option<out T>
data class Just<T>(val value: T) : Option<T>()
object None : Option<Nothing>()
Just or None
203. Algebraic Data Type
• Pattern matching (Product Type)
val rgb: RGBInterface = Red()
val result = when (rgb) {
is Red -> "Red"
is Green -> "Green"
is Blue -> "Blue"
else -> throw IllegalArgumentException()
}
204. Algebraic Data Type
• Pattern matching (Product Type)
val rgb: RGBInterface = Red()
val result = when (rgb) {
is Red -> "Red"
is Green -> "Green"
is Blue -> "Blue"
else -> throw IllegalArgumentException()
}
Pink 가 추가 된다면?
205. Algebraic Data Type
• Pattern matching (Product Type)
interface ColorInterface
class Red : ColorInterface
class Green : ColorInterface
class Blue : ColorInterface
class Pink: ColorInterface
206. Algebraic Data Type
• Pattern matching (Product Type)
val rgb: ColorInterface = Pink()
val result = when (rgb) {
is Red -> "Red"
is Green -> "Green"
is Blue -> "Blue"
else -> throw IllegalArgumentException()
}
207. Algebraic Data Type
• Pattern matching (Product Type)
val rgb: ColorInterface = Pink()
val result = when (rgb) {
is Red -> "Red"
is Green -> "Green"
is Blue -> "Blue"
else -> throw IllegalArgumentException()
}
Compile ok
208. Algebraic Data Type
• Pattern matching (Product Type)
val rgb: ColorInterface = Pink()
val result = when (rgb) {
is Red -> "Red"
is Green -> "Green"
is Blue -> "Blue"
else -> throw IllegalArgumentException()
}
Runtime crash
209. Algebraic Data Type
• Pattern matching (Product Type)
val rgb: ColorInterface = Pink()
val result = when (rgb) {
is Red -> "Red"
is Green -> "Green"
is Blue -> "Blue"
else -> throw IllegalArgumentException()
}
Runtime crash
211. Algebraic Data Type
• Pattern matching (Sum Type)
val rgb2: RGB = RGB.RED
val result2 = when (rgb2) {
RGB.RED -> "Red"
RGB.BLUE -> "Blue"
RGB.GREEN -> "Green"
}
212. Algebraic Data Type
• Pattern matching (Sum Type)
val rgb2: RGB = RGB.RED
val result2 = when (rgb2) {
RGB.RED -> "Red"
RGB.BLUE -> "Blue"
RGB.GREEN -> "Green"
}
ProductType가 다르게 else가 필요 없다.
213. Algebraic Data Type
• Pattern matching (Sum Type)
val rgb2: RGB = RGB.RED
val result2 = when (rgb2) {
RGB.RED -> "Red"
RGB.BLUE -> "Blue"
RGB.GREEN -> "Green"
}
ProductType가 다르게 else가 필요 없다.
Pink 가 추가 된다면?
214. Algebraic Data Type
• Pattern matching (Sum Type)
enum class COLOR {
RED, GREEN, BLUE, PINK
}
215. Algebraic Data Type
• Pattern matching (Sum Type)
val rgb2: Color = Color.PINK
val result2 = when (rgb2) {
Color.RED -> "Red"
Color.BLUE -> "Blue"
Color.GREEN -> "Green"
}
Compile error
216. Algebraic Data Type
• Pattern matching (Sum Type)
val rgb2: Color = Color.PINK
val result2 = when (rgb2) {
Color.RED -> "Red"
Color.BLUE -> "Blue"
Color.GREEN -> "Green"
}
Compile error
217. Algebraic Data Type
• Pattern matching (Sum Type)
val rgb2: Color = Color.PINK
val result2 = when (rgb2) {
Color.RED -> "Red"
Color.BLUE -> "Blue"
Color.GREEN -> "Green"
}
Compile error
컴파일 타임에 에러를 방지할 수 있다.
218. Algebraic Data Type
• Pattern matching (Sum Type)
val rgb2: Color = Color.PINK
val result2 = when (rgb2) {
Color.RED -> "Red"
Color.BLUE -> "Blue"
Color.GREEN -> "Green"
Color.PINK -> "Pink"
}
Compile ok
222. Exception
• none pureFunction
fun main(args: Array<String>) {
val a = 5
val result = func(a)
println(result) // 2
}
fun func(value: Int): Int {
return 10 / value
}
223. Exception
• none pureFunction
fun main(args: Array<String>) {
val b = 0
val result = func(b)
println(result)
// java.lang.ArithmeticException: / by zero
}
fun func(value: Int): Int {
return 10 / value
}
224. Exception
• partialFunction
fun main(args: Array<String>) {
val b = 0
val result = func(b)
println(result)
// java.lang.ArithmeticException: / by zero
}
fun func(value: Int): Int {
return 10 / value
}
225. Exception
• partialFunction
fun func(value: Int): Int {
if(value == 0){
return -1
} else {
return 10 / value
}
}
fun func2(value: Int): Int? {
if(value == 0){
return null
} else {
return 10 / value
}
}
fun func3(value: Int): Int {
if(value == 0){
return throw ArithmeticException("")
} else {
return 10 / value
}
}
fun func4(value: Int): Int {
return try {
10 / value
} catch (e: Exception) {
-1
}
}
226. Exception
• partialFunction
fun func(value: Int): Int {
if(value == 0){
return -1
} else {
return 10 / value
}
}
fun func2(value: Int): Int? {
if(value == 0){
return null
} else {
return 10 / value
}
}
fun func3(value: Int): Int {
if(value == 0){
return throw ArithmeticException("")
} else {
return 10 / value
}
}
fun func4(value: Int): Int {
return try {
10 / value
} catch (e: Exception) {
-1
}
}
227. Exception
• partialFunction
fun func(value: Int): Int {
if(value == 0){
return -1
} else {
return 10 / value
}
}
fun func2(value: Int): Int? {
if(value == 0){
return null
} else {
return 10 / value
}
}
fun func3(value: Int): Int {
if(value == 0){
return throw ArithmeticException("")
} else {
return 10 / value
}
}
fun func4(value: Int): Int {
return try {
10 / value
} catch (e: Exception) {
-1
}
}
228. Exception
• partialFunction
fun func(value: Int): Int {
if(value == 0){
return -1
} else {
return 10 / value
}
}
fun func2(value: Int): Int? {
if(value == 0){
return null
} else {
return 10 / value
}
}
fun func3(value: Int): Int {
if(value == 0){
return throw ArithmeticException("")
} else {
return 10 / value
}
}
fun func4(value: Int): Int {
return try {
10 / value
} catch (e: Exception) {
-1
}
}
230. Exception
• partialFunction
fun main(args: Array<String>) {
val a = 0
val result = func(a)
val b = 10
val result2 = func2(result, b)
println(result2) // -10
}
fun func(value: Int): Int {
return try {
10 / value
} catch (e: Exception) {
-1
}
}
fun func2(x: Int, y: Int): Int {
return x * y
}
231. Exception
• partialFunction
fun func(value: Int): Int {
return try {
10 / value
} catch (e: Exception) {
-1
}
}
fun func2(x: Int, y: Int): Int {
return x * y
}
fun main(args: Array<String>) {
val a = 0
val result = func(a)
val b = 10
val result2 = func2(result, b)
println(result2) // -10
}
232. Exception
• partialFunction
fun func(value: Int): Int {
return try {
10 / value
} catch (e: Exception) {
-1
}
}
fun func2(x: Int, y: Int): Int {
return x * y
}
fun main(args: Array<String>) {
val a = 0
val result = func(a)
val b = 10
val result2 = if (result != -1) {
func2(result, b)
} else {
-1
}
println(result2) // -1
}
233. Exception
• partialFunction
fun func(value: Int): Int {
return try {
10 / value
} catch (e: Exception) {
-1
}
}
fun func2(x: Int, y: Int): Int {
return try {
x * y
} catch (e: Exception) {
-1
}
}
fun main(args: Array<String>) {
val a = 0
val result = func(a)
val b = 10
val result2 = if (result != -1) {
func2(result, b)
} else {
-1
}
println(result2) // -1
}
234. Exception
• partialFunction
• -1을 예외상황에 사용한다면 실제값이 -1인 상황을 예외로 인식
할수 있다.
• Null 이나 exception을 사용하면 순수함수라고 할 없으며, 비즈
니스 로직의 흐름이 끊겨버린다.
• 앞의 결과를 다음 로직에 사용 하려면 항상 값 체크를 해줘야 한
다.
236. Exception
• Maybe
sealed class Maybe<out T>
data class Just<T>(val value: T) : Maybe<T>()
object None : Maybe<Nothing>()
237. Exception
• Maybe
sealed class Maybe<out T>
data class Just<T>(val value: T) : Maybe<T>()
object None : Maybe<Nothing>()
• 값이 있으면 Just
• 값이 없으면 None
241. Exception
• Maybe
fun main(args: Array<String>) {
val a = 0
val result = func(a)
val b = 10
val result2 = if (result != -1) {
func2(result, b)
} else {
-1
}
println(result2) // -1
}
242. Exception
• Maybe
fun main(args: Array<String>) {
val a = 0
val result = func(a)
val b = 10
val result2 = when (result) {
is Just -> func2(result.value, b)
None -> None
}
when(result2){
is Just -> println(result2.value)
None -> println("none")
}
}
243. Exception
• Maybe
fun main(args: Array<String>) {
val a = 0
val result = func(a)
val b = 10
val result2 = when (result) {
is Just -> func2(result.value, b)
None -> None
}
when(result2){
is Just -> println(result2.value)
None -> println("none")
}
}
244. Exception
• Maybe
sealed class Maybe<out T>
data class Just<T>(val value: T) : Maybe<T>()
object None : Maybe<Nothing>()
245. Exception
• Maybe
sealed class Maybe<out T> {
abstract fun <R> map(transformer: (T) -> R): Maybe<R>
}
data class Just<T>(val value: T) : Maybe<T>() {
override fun <R> map(transformer: (T) -> R): Just<R> =
Just(transformer(value))
}
object None : Maybe<Nothing>() {
override fun <R> map(transformer: (Nothing) -> R): Maybe<R> =
None
}
246. Exception
• Maybe
sealed class Maybe<out T> {
abstract fun <R> map(transformer: (T) -> R): Maybe<R>
}
data class Just<T>(val value: T) : Maybe<T>() {
override fun <R> map(transformer: (T) -> R): Just<R> =
Just(transformer(value))
}
object None : Maybe<Nothing>() {
override fun <R> map(transformer: (Nothing) -> R): Maybe<R> =
None
}
247. Exception
• Maybe
sealed class Maybe<out T> {
abstract fun <R> map(transformer: (T) -> R): Maybe<R>
}
data class Just<T>(val value: T) : Maybe<T>() {
override fun <R> map(transformer: (T) -> R): Just<R> =
Just(transformer(value))
}
object None : Maybe<Nothing>() {
override fun <R> map(transformer: (Nothing) -> R): Maybe<R> =
None
}
248. Exception
• Maybe
fun main(args: Array<String>) {
val a = 0
val result = func(a)
val b = 10
val result2 = if (result != -1) {
func2(result, b)
} else {
-1
}
println(result2) // -1
}
249. Exception
• Maybe
fun main(args: Array<String>) {
val a = 0
val result = func(a)
val b = 10
val result2 = result.map { value -> func2(value, b) }
when (result2) {
is Just -> println(result2.value)
None -> println("none")
}
}
250. Exception
• Maybe
val result = func(0)
val result2 = if (result != -1) {
func2(result, 10)
} else {
-1
}
val result3 = if (result2 != -1) {
func2(result2, 0)
} else {
-1
}
val result4 = if (result3 != -1) {
func2(result3, 4)
} else {
-1
}
println(result4) // -1
256. 실습
• FpList<Int>의 모든 요소들의 합을 구하는 sum 함수를 일반재귀,
꼬리재귀로 만들어보자.
fun FpList<Int>.sum(): Int = when (this) {
Nil -> 0
is Cons -> head + tail.sum()
}
tailrec fun FpList<Int>.tailrecSum(acc: Int): Int =
when (this) {
Nil -> acc
is Cons -> tailrecSum(head + acc)
}
257. 실습
• FpList의 요소중 앞에서부터 n개를 제외하는 drop 함수를 만들자.
• 요소의 개수가 n보다 크다면 요소의 빈리스트를
• 빈리스트면 n과 상관없이 빈리스트를 반환
258. 실습
• FpList의 요소중 앞에서부터 n개를 제외하는 drop 함수를 만들자.
• 요소의 개수가 n보다 크다면 요소의 빈리스트를
• 빈리스트면 n과 상관없이 빈리스트를 반환
tailrec fun <T> FpList<T>.drop(n: Int): FpList<T> = when(this){
Nil -> Nil
is Cons -> when(n){
0 -> this
else -> tail.drop(n-1)
}
}
259. 실습
• FpList의 요소중 앞에서부터 n개를 반환하는 take 함수를 만들자.
• 요소의 개수가 n보다 작다면 요소의 개수만큼 반환
• 빈리스트면 n과 상관없이 빈리스트를 반환
260. 실습
• FpList의 요소중 앞에서부터 n개를 반환하는 take 함수를 만들자.
• 요소의 개수가 n보다 작다면 요소의 개수만큼 반환
• 빈리스트면 n과 상관없이 빈리스트를 반환
fun <T> FpList<T>.take(n: Int): FpList<T> = when (this) {
Nil -> Nil
is Cons -> when (n) {
0 -> Nil
else -> Cons(head, tail.take(n - 1))
}
}
tailrec fun <T> FpList<T>.take(n: Int, acc: FpList<T>): FpList<T> = when (this) {
Nil -> acc
is Cons -> when (n) {
0 -> acc
else -> tail.take(n - 1, acc.appendTail(head))
}
}
261. 실습
• 타입을 변환해주는 transformer 함수를 입력받아 다른 타입의
FpList로 반환해주는 map 함수를 일반 재귀와 꼬리재귀로 만들자.
262. 실습
• 타입을 변환해주는 transformer 함수를 입력받아 다른 타입의
FpList로 반환해주는 map 함수를 일반 재귀와 꼬리재귀로 만들자.
fun <T, R> FpList<T>.map(transformer: (T) -> R): FpList<R> =
when (this) {
Nil -> Nil
is Cons -> Cons(transformer(head), tail.map(transformer))
}
tailrec fun <T, R> FpList<T>.map(acc: FpList<R>, transformer: (T) -> R): FpList<R> =
when (this) {
Nil -> acc
is Cons -> tail.map(acc.appendTail(transformer(head)), transformer)
}
263. 실습
• boolean을 반환하는 predicate 함수를 입력받아 요소들 중 참인
요소들만 걸러내는 filter 함수를 일반 재귀와 꼬리재귀로 만들자.
264. 실습
• boolean을 반환하는 predicate 함수를 입력받아 요소들 중 참인
요소들만 걸러내는 filter 함수를 일반 재귀와 꼬리재귀로 만들자.
fun <T> FpList<T>.filter(predicate: (T) -> Boolean): FpList<T> =
when (this) {
Nil -> Nil
is Cons -> if (predicate(head)) {
Cons(head, tail.filter(predicate))
} else {
tail.filter(predicate)
}
}
tailrec fun <T> FpList<T>.filter(acc: FpList<T>, predicate: (T) -> Boolean): FpList<T>
when (this) {
Nil -> acc
is Cons -> if (predicate(head)) {
tail.filter(acc.appendTail(head), predicate)
} else {
tail.filter(acc, predicate)
}
}
265. • 사이드 이펙트를 방지 할 수 있습니다.
• 일반적으로 코드가 간결해집니다.
• 코드의 재활용성이 높아집니다.
• 게으른 평가가 가능(LazyEvaluation)
• 타입 추론이 가능
장점
267. • 일반적으로 코드가 간결해집니다.
장점
fun unSimple(list: List<Int>): List<Int> {
val evens = mutableListOf<Int>()
for (elem in list) {
if (elem % 2 == 0) {
evens.add(elem)
}
}
val result = mutableListOf<Int>()
for (elem in evens) {
result.add(elem * 10)
}
return result
}
268. • 일반적으로 코드가 간결해집니다.
장점
fun simple(list: List<Int>): List<Int> = list
.filter { it % 2 == 0 }
.map { it * 10 }
269. • 일반적으로 코드가 간결해집니다.
장점
fun main(args: Array<String>) {
val list = listOf(1, 2, 3, 4, 5)
println(unSimple(list)) //[20, 40]
println(simple(list)) //[20, 40]
}
fun unSimple(list: List<Int>): List<Int> {
val evens = mutableListOf<Int>()
for (elem in list) {
if (elem % 2 == 0) {
evens.add(elem)
}
}
val result = mutableListOf<Int>()
for (elem in evens) {
result.add(elem * 10)
}
return result
}
fun simple(list: List<Int>): List<Int> = list
.filter { it % 2 == 0 }
.map { it * 10 }