수학적 함수와 명령형 프로그래밍에서 사용되는 함수
- 명령형 프로그래밍의 함수
- 프로그램의 상태 값을 바꿀 수 있는 부수 효과가 생길 수 있음
- 참조 투명성이 없고, 같은 코드라도 실행되는 프로그램의 상태에 따라 다른 결과값이 나옴
- 함수형 프로그래밍의 함수(수학적 함수)
- 함수의 출력 값은 함수에 입력된 인수에만 의존
- 인수 x에 같은 값을 넣고 함수 f를 호출하면 항상 f(x)라는 결과가 나옴(참조 투명성)
- 부수 효과를 제거하면 프로그램 동작 이해와 예측이 쉬움
함수형 프로그래밍 기본 요소
- pure functino
- 부수 효과(side-effect)가 없는 함수
- thread-safe하여 병렬 계산 용이
- anoymous function: 익명 함수 (람다 함수)
- 코틀린에서는 x → x*x
- higher-order functino: 고차(고계) 함수, 함수를 인자나 리턴으로 다루는 함수
- 코틀린에서는 (1.10).map{it * it}
+& 코틀린은 순수한 함수형 언어가 아님, 함수형 언어의 요소 뿐 아니라 명령형 언어, 객체 언어 패러다임을 모두 가지고 있음
Lambda
data class MyClass(val a:Int, val b:String)
fun lambdaTest(a:(Int) -> Int): Int{
return a(10)
}
fun main()
{
val sum = {x:Int, y:Int -> x+y}
println("sum = ${sum(10,20)}")
val array = arrayOf(MyClass(10,"class1"),MyClass(20,"class2"),MyClass(30,"class3"))
println(array.filter({c:MyClass -> c.a < 15}))
array.filter() {c:MyClass -> c.a < 15 }
array.filter { c -> c.a <15 }
// 람다 파라미터가 하나일 경우 it 으로 생략이 가능함
array.filter { it.a < 15 }
print(lambdaTest { it+10 })
val title = "Num:"
val list = listOf(1,2,3,4)
list.forEach{ println("$title $it")}
}
- 어원: 람다 대수
- 이름이 없는 함수
- 형식: {파라미터 → 함수 바디}
- {x: Int, y: Int → x+y}
- 함수 바디에서 마지막 수식(expression)이 리턴 값이 됨
- Lambda에서 로컬 변수 참조
- val, var 구분 없이 모든 로컬 변수 참조가 가능함
Collection filter, map, groupBy와 lambda
data class Student(val name: String,val age:Int)
fun main(){
val data = listOf(Student("Jun",21),Student("James",25)
,Student("Tom",21),Student("Jane",23),Student("John",23))
println(data.filter { it.age >=22})
println(data.map{it.age - 20})
println(data.filter { it.age>=22 }.map (Student::name))
// data.filter{it.age>22}.map{it.name}
println(data.groupBy { it.age })
val words = arrayOf("hello","hi","hot","aple","orange","access","order","about")
println(words.groupBy { it.first() })
}
- 함수형 프로그래밍에서 Collection을 다루는 방법
- lambda가 편리하게 사용됨
- filter, map
- filter: 특정 조건을 만족하는 원소만 포함하는 Collection을 생성/리턴
- filter에 주어진 람다를 모든 원소에 수행하여 true를 리턴하는 경우만 모음
- filter에 넘겨주는 람다는 Boolean을 결과로 하는 수식이어야 함
- map: 모든 원소에 대해 특정 연산을 수행한 결과를 모아서 Collection을 생성/리턴
- map에 주어진 람다를 모든 원소에 대해 수행하고 그 결과를 모음
- groupBy: 주어진 조건에 따라 Collection을 그룹으로 나눈 후 map을 생성/리턴
- 참고: Collection은 언어 문법이 아니고 표준 라이브러리임
Collection all, any, count, find와 lambda
fun main(){
val nums = arrayOf(-5,-4,-3,-2,-1,0,1,2,3,4,5)
println(nums.all { it is Int })
println(nums.all {it >0})
println(nums.any{it>0})
println(nums.count{it>0})
println(nums.find{it>0})
}
- all, any, count, find
- all: 모든 원소가 특정 조건을 만족하면 true, 그렇지 않으면 false
- any: 한 원소라도 특정 조건을 만족하면 true, 그렇지 않으면 false
- count: 특정 조건을 만족하는 원소의 갯수를 리턴
- find: 특정 조건을 만족하는 가장 처음 원소를 리턴
Collection flatMap과 lambda
fun main(){
val lsit = listOf("abc","cde","efg")
println(lsit.flatMap { it.toList() })
println(lsit.flatMap { it.toList() }.toSet())
class Classes(val name:String, val students:List<String>)
val classes = listOf(Classes("Cprog", listOf("james","john","greg")),
Classes("OS", listOf("james","john","jane","ton")),
Classes("Net",listOf("john","jane","alex","sam")))
println(classes.flatMap { it.students}.toSet().sorted())
}
- 중첩된 Collection을 하나의 리스트로 생성/리턴
- flatMap에 주어진 람다는 iterable 객체를 리턴
- 이 iterable을 모두 연결하여 하나의 List로 만든다.
Collection asSequence
fun main(){
val hugeNums = (1..1000000).toList()
val elapsedTime = measureTimeMillis {
hugeNums.map{it*it}.filter { it>10 }.map { it*it }
}
val elapsedTime2 = measureTimeMillis {
hugeNums.asSequence().map { it*it }.filter { it>10 }.map { it*it }
}
val elapsedTime3 = measureTimeMillis {
hugeNums.asSequence().map { it*it }.filter { it>10 }.map { it*it }.toList()
}
println("elapsedTime = ${elapsedTime}")
println("elapsedTime2 = ${elapsedTime2}")
println("elapsedTime3 = ${elapsedTime3}")
}
- filter, map 등의 함수를 부를 때, 바로 다른 리스트를 생성하게 됨
- 이런 함수를 매우 큰 데이터에 대해 여러 번 이어서 사용하면 매우 느려질 것임
- 이런 경우 Sequence를 사용하면
- 리스트 생성을 최대한 늦추게 하는 방법임
- 실제 리스트 생성 작업을 최대한 늦추기 때문에 lazy 연산이라고 보통 부름
- Seqence를 사용하여 연산을 끝낸 후에, 다시 toList()로 Collection을 바꾸어 사용함
lambda와 SAM
fun interface doInterface{
fun doIt()
}
fun doSomething(di:doInterface)=di.doIt()
fun main(){
doSomething(object :doInterface{
override fun doIt() {
println("Java-like way")
}
})
doSomething(doInterface { println("SAM") })
// 익명 함수 생략
doSomething{ println("SAM 1")}
val doi = { println("SAM 2")}
doSomething(doi)
}
- SAM: Single Abstract Method의 줄임말, 인터페이스가 하나의 메소드만 가진 경우
- View.OnClickListener, Runnable 등 많은 인터페이스가 하나의 메소드만 가짐
- 코틀린에서 이런 SAM인 경우 무명 클래스 인스턴스를 만드는 것이 아니라 lambda로 처리할 수 있음
Scope 함수
fun main(){
val r1 = "hello".let {
println("it = ${it}")
it.length
}
val r2 = "hello".run {
println("this = ${this}")
this.length
}
val r3 = "hello".also {
println("it = ${it}")
}.length
println("r1 = ${r1}")
println("r2 = ${r2}")
println("r3 = ${r3}")
}
fun main(){
val str = with(StringBuilder()){
// this. 생략
append("Hello, ")
append("Hello, ")
append("Hello, ")
append("Hello, ")
toString()
}
println(str)
val str2 = StringBuilder().apply{
append("Hello, ")
append("Hello, ")
append("Hello, ")
append("Hello, ")
}.toString()
println("str2 = ${str2}")
}
- 객체의 이름을 반복하지 않고 그 객체에 대해 여러 연산을 수행할 수 있음
- 종류: let, run, with, apply, alse
- 주의: with 함수는 주어진 객체를 이용하여 블록 안에서 수행하는 작업을 처리한 후, 마지막 표현식의 값을 반환한다. (unit)
- 주의: apply 함수는 주어진 객체를 이용하여 블록 안에서 수행하는 작업을 처리한 후, 객체 자신을 반환한다. (this)
- 함수 인자로 lambda를 전달하는 데, 이 lambda 내에서 객체를 it으로 또는 this로 지칭
- 함수의 리턴 값은 객체 자체 또는 lambda의 결과
- 참고: Scope 함수는 언어 문법이 아니고 표준 라이브러리임
Collectino 연산자 오버로딩
data class Three(var x:Int, var y:Int, var z:Int):Iterable<Int>{
operator fun get(idx:Int):Int{
return when(idx){
0 -> x
1 -> y
2 -> z
else ->
throw IndexOutOfBoundsException("Invaild index $idx")
}
}
operator fun set(idx:Int, value:Int){
when(idx){
0 -> x = value
1 -> y = value
2 -> z = value
else ->
throw IndexOutOfBoundsException("Invaild index $idx")
}
}
operator fun contains(value:Int) = (x==value||y==value||z==value)
inner class MyIterator:Iterator<Int>{
var curIdx = 0
override fun next(): Int {
val ret = this@Three[curIdx]
curIdx++
return ret
}
override fun hasNext(): Boolean {
return curIdx <= 2
}
}
override fun iterator(): Iterator<Int> = MyIterator()
}
fun main(){
val three = Three(1,2,3)
println("three[0] = ${three[0]}")
println("three[1] = ${three[1]}")
println("three[2] = ${three[2]}")
println((3 in Three(1,2,3)))
for (i in three)
println(i)
}
- []
- get(idx), set(idx, value)
- in
- contains(value)
- iterator
- iterator Implementation
- iterator method
가변 인자
fun main(){
val list = listOf(1,2,3,4)
val args = arrayOf("1","2","3","4")
val list2 = listOf("1",*args)
println(list2)
}
- 가변 인자
- varag를 매개 변수 이름 앞에 사용함
- spread 연산자: *를 배열앞에 붙여서 가변 인자를 넘겨줄 수 있음
Infix Function
fun main(){
// Any를 확장 하여 infix to를 정의한 것
infix fun Any.to(other: Any) = Pair(this,other)
}
- 함수 이름을 인자 중간에 넣어서 호출
- 예) mapOf(1 to “one, 2 to “two”) 에서 to와 같은 함수
- fun plus(1,2) → fun 1 plus 2
Destructuring declaration
data class Result(val result:Int, val status:String)
fun calcSomething(): Result{
return Result(404,"Not Found")
}
fun main(){
val (num,str) = 1 to "one"
println("$num, $str")
val collection = mapOf(1 to "one", 2 to "two")
for((a,b)in collection){
println("$a, $b")
}
val (result, status) = calcSomething()
println("$result,$status")
}
- 값 2개를 리턴 받아 2개의 변수에 대입
'Development > Kotlin&Android' 카테고리의 다른 글
액티비티와 인텐트 (0) | 2023.04.13 |
---|---|
안드로이드-레이아웃 (0) | 2023.03.30 |
Kotlin 문법 Part 3 (0) | 2023.03.16 |
Kotlin 문법 Part 2 (0) | 2023.03.02 |
Kotlin 문법 Part 1 (0) | 2023.02.28 |