일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- function
- animation
- pushnamed
- 코틀린
- set
- 웹크롤러
- 크롤러
- Yocto
- import
- ML
- text
- Class
- map
- 플러터
- Android
- DART
- 클래스
- crawler
- 콜렉션
- 함수
- 다트
- kotlin
- Collection
- python
- List
- textstyle
- variable
- Flutter
- package
- 파이썬
- Today
- Total
조용한 담장
코틀린(Kotlin) : 함수(Function) 본문
코틀린(kotlin) 의 함수 (funciton) 에 대해 살펴보자.
원문 https://kotlinlang.org/docs/reference/functions.html 을 보며 정리.
Function declarations
함수 정의는 fun 키워드를 사용한다.
fun double(x: Int): Int {
return 2 * x
}
Function usage
함수 호출
val result = double(2)
멤버 함수 호출
Stream().read() // create instance of class Stream and call read()
Parameters
함수 파라미터는 Pascal notation 을 써서 정의한다.
각 파라미터는 type 이 정의되어야 한다.
fun powerOf(number: Int, exponent: Int) { /*...*/ }
Default arguments
함수 파라미터는 argument 가 생략 된 경우에 할당되는 해당 파라미터의 기본 값을 가질 수 있다.
fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size) { /*...*/ }
기본값은 type 과 값 사이에 = 를 사용하여 정의한다.
overriding method 는 항상 base method 와 같은 기본 파라미터 값을 쓴다.
기본 파라미터 값을 가진 method 를 overriding 할 때는 기본 값 할당 표현을 생략할 수 있다.
open class A {
open fun foo(i: Int = 10) { /*...*/ }
}
class B : A() {
override fun foo(i: Int) { /*...*/ } // no default value allowed
}
함수의 인자 항목에서 기본 값이 있는 파라미터가 기본 값이 없는 파라미터 보다 앞에 위치하면, 기본 값은 named arguments (아래에서 설명) 로 함수를 호출할 때만 쓰이게 된다.
fun foo(bar: Int = 0, baz: Int) { /*...*/ }
foo(baz = 1) // The default value bar = 0 is used
함수의 인자 항목에서 기본값을 가진 파라미터 뒤에 위치한 마지막 인자가 lambda 인 경우, named argument 나 outside the parentheses 로 전달 될 수 있다.
fun foo(bar: Int = 0, baz: Int = 1, qux: () -> Unit) { /*...*/ }
foo(1) { println("hello") } // Uses the default value baz = 1
foo(qux = { println("hello") }) // Uses both default values bar = 0 and baz = 1
foo { println("hello") } // Uses both default values bar = 0 and baz = 1
Named arguments
함수 호출 시 파라미터의 이름을 사용할 수 있다.
fun reformat(str: String,
normalizeCase: Boolean = true,
upperCaseFirstLetter: Boolean = true,
divideByCamelHumps: Boolean = false,
wordSeparator: Char = ' ') {
/*...*/
}
기본 arguments 와 사용할 때
reformat(str)
기본 값을 사용하지 않을 때
reformat(str, true, true, false, '_')
보기 좋게
reformat(str,
normalizeCase = true,
upperCaseFirstLetter = true,
divideByCamelHumps = false,
wordSeparator = '_'
)
일부 arguments 만 지정할 때
reformat(str, wordSeparator = '_')
함수가 positional and named arguments 와 함게 호출되면, 모든 positional arguments 는 첫번째 named argument 앞에 위치헤야 한다.
예를들어 f(1, y = 2) 는 맞지만, f(x = 1, 2) 은 틀리게 된다.
Variable number of arguments (vararg, 아래에서 설명) 은 spread operator 를 사용하여 named 형식으로 전달될 수 있다.
fun foo(vararg strings: String) { /*...*/ }
foo(strings = *arrayOf("a", "b", "c"))
On the JVM: the named argument syntax cannot be used when calling Java functions because Java bytecode does not always preserve names of function parameters.
Unit-returning functions
함수가 아무 의미있는 값을 리턴하지 않을 때의 return type 은 Unit 이 된다.
Unit 은 단 하나의 값(Unit) 을 가진 타입 이다.
이 값은 항상 리턴할 필요는 없다.
fun printHello(name: String?): Unit {
if (name != null)
println("Hello ${name}")
else
println("Hi there!")
// `return Unit` or `return` is optional
}
Unit 리턴 타입 정의는 생략해도 되며 위의 코드는 아래 코드와 동일하다.
fun printHello(name: String?) { ... }
Single-expression functions
함수가 single expression 을 리턴할 때에는 { } 는 생략될 수 있고 함수의 바디는 = 뒤에 위치한다.
fun double(x: Int): Int = x * 2
컴파일러가 expression 에서 리턴 타입을 유추할 수 있는 경우에는 return type 을 생략할 수 있다.
fun double(x: Int) = x * 2
Explicit return types
함수의 block body 가 있는 경우엔 항상 return type 을 명시해야 하며 그렇지 않으면 Unit 을 리턴하는 것으로 간주된다.
코틀린은 block body 가 있는 함수는 return type 을 유추하지 않는데, 이는 함수의 코드가 복잡하거나 return type 을 명확히 판단할 수 없기 때문이다.
Variable number of arguments (Varargs)
함수의 파라미터 (보통 마지막에 오는) 는 vararg 로 표시될 수 있는데, 여러개의 argument 를 함수에 전달할 수 있게 해
준다.
fun <T> asList(vararg ts: T): List<T> {
val result = ArrayList<T>()
for (t in ts) // ts is an Array
result.add(t)
return result
}
val list = asList(1, 2, 3)
함수 안에서 T 타입의 vararg 파리미터는 T 값들을 가진 array 로 보이게 되며, ts 변수는 Array<out T> 타입을 가진다.
한개의 파라미터만 vararg 로 표시될 수 있다.
vararg 파라미터가 함수 인자 목록의 마지막이 아닌 경우에는 그 뒤의 파라미터의 값들을 named argument 를 써서 전달하거나, 파라미터가 funciton type 이면 ( ) 밖에 lambda 를 써서 전달할 수 있다.
vararg 함수를 호출할 때 asList(1, 2, 3) 처럼 argument 를 하나씩 전달하거나, 이미 array 가 있고 그 내용을 함수에 전달하고 싶을때는 spread operator * 를 array 의 prefix 로 사용할 수 있다.
val a = arrayOf(1, 2, 3)
val list = asList(-1, 0, *a, 4)
Infix notation
infix 키워드를 가진 함수는 . 과 ( ) 를 생략하고 호출하는 infix notation 을 사용하여 호출될 수 있다.
infix function 은 아래의 사항을 만족해야 한다.
- 멤버 함수 이거나 확장 함수 이어야 한다.
- 단 하나의 파라미터를 가진다.
- 파라미터는 고정되지 않은 개수의 arugment 를 가질 수 없고 기본 값을 가질 수 없다.
infix fun Int.shl(x: Int): Int { ... }
// calling the function using the infix notation
1 shl 2
// is the same as
1.shl(2)
infix function 호출은 arithmetic operators, type casts, and the rangeTo operator 보다 우선순위가 낮다.
아래의 예제들은 모두 동등하다.
- 1 shl 2 + 3 is equivalent to 1 shl (2 + 3)
- 0 until n \* 2 is equivalent to 0 until (n * 2)
- xs union ys as Set<\*> is equivalent to xs union (ys as Set<\*>)
boolean operators && and ||, is- and in-checks, and some other operators 보다는 우선 순위가 높다.
아래의 예제들은 모두 동등하다.
- a && b xor c is equivalent to a && (b xor c)
- a xor b in c is equivalent to (a xor b) in c
참조 : Grammar reference
infix funcitons 는 항상 receiver 와 지정된 파라미터가 필요하다.
infix notation 을 사용하여 현재의 receiver 에서 method 를 호출할 때는 this 를 명확히 써야 하며 일반 method 호출과 다르게 생략될 수 없다.
class MyStringCollection {
infix fun add(s: String) { /*...*/ }
fun build() {
this add "abc" // Correct
add("abc") // Correct
//add "abc" // Incorrect: the receiver must be specified
}
}
Function scope
코틀린에서 함수는 파일의 상위 레벨에 정의될 수 있는데 이는 함수가 반드시 클래스에 속할 필요는 없다는 뜻이다.
멤버 함수 와 확장 함수로써 로컬(중첩)의 위치에 선언될 수 있다.
Local functions
코틀린은 함수 안의 함수를 지원한다.
fun dfs(graph: Graph) {
fun dfs(current: Vertex, visited: MutableSet<Vertex>) {
if (!visited.add(current)) return
for (v in current.neighbors)
dfs(v, visited)
}
dfs(graph.vertices[0], HashSet())
}
로컬 함수는 외부 함수의 변수에 접근할 수 있다.
fun dfs(graph: Graph) {
val visited = HashSet<Vertex>()
fun dfs(current: Vertex) {
if (!visited.add(current)) return
for (v in current.neighbors)
dfs(v)
}
dfs(graph.vertices[0])
}
Member functions
멤버 함수는 클래스나 오브젝트의 내부에 정의된다.
class Sample() {
fun foo() { print("Foo") }
}
Sample().foo() // creates instance of class Sample and calls foo
Generic functions
함수 이름 앞에 < > 를 사용하여 지정된 generic parameters 를 가질 수 있다.
fun <T> singletonList(item: T): List<T> { /*...*/ }
Inline functions
다음에 ...
Extension functions
참조 : link
Higher-order functions and lambdas
다음에 ...
Tail recursive functions
코틀린은 tail recursion 라고 알려진 함수형 프로그래밍 스타일을 지원한다.
이는 특정 알고리즘의 경우 재귀형 함수 대신 루프를 사용할 수 있고, stack overflow 의 위험을 피할 수 있게 해준다.
함수가 tailrec 로 표시되고 특정 조건들을 만족할 때 컴파일러는 재귀구문을 효과적인 루프 기반의 버전으로 최적화 한다.
val eps = 1E-10 // "good enough", could be 10^-15
tailrec fun findFixPoint(x: Double = 1.0): Double
= if (Math.abs(x - Math.cos(x)) < eps) x else findFixPoint(Math.cos(x))
이 코드는 Math.cos 를 1.0 에서 시작해서 결과가 더이상 바뀌지 않을때까지 반복적으로 호출하여 eps 0.7390851332151611 값을 넘긴다.
이는 좀더 전통적인 아래의 코드와 동등하다.
val eps = 1E-10 // "good enough", could be 10^-15
private fun findFixPoint(): Double {
var x = 1.0
while (true) {
val y = Math.cos(x)
if (Math.abs(x - y) < eps) return x
x = Math.cos(x)
}
}
tailrec 에 좀더 맞게 하려면 함수는 자신이 마지막 호출 대상이 되어야 한다.
재귀 호출 이후의 더이상의 코드가 없어야 tail recursion 을 쓸 수 있으며, try/catch/finally 구문과 함께 쓸 수 없다.
'kotlin' 카테고리의 다른 글
코틀린(Kotlin) : 인라인 함수 (Inline Functions) (0) | 2020.01.02 |
---|---|
코틀린(Kotlin) : 고차함수 와 람다 (Higher-Order Functions and Lambdas) (0) | 2020.01.02 |
코틀린(Kotlin) 클래스(Class) : 위임(Delegation) (0) | 2020.01.02 |
코틀린(Kotlin) 클래스(Class) : 객체 표현식 과 선언 (Object Expressions and Declarations) (1) | 2020.01.02 |
코틀린(Kotlin) 클래스(Class) : 제네릭(Generics) (0) | 2020.01.02 |