快速简单入门Kotlin——函数入门(第二天)

我们在前面已经学习了Kotlin基础,主要介绍各种变量及其使用方法,本节我们将介绍kotlin的函数。

1.创建和使用函数

函数我们一早就见过,程序的入口点在 main。println 也是函数调用,不过这个函数是标准库中已经实现好了的,现在是我们在调用这个函数

fun main() {
    println("Hello World")
}

函数就是定义在类中的具有特定功能的一段独立小程序,函数也称为方法。

那我们来看看,如何创建和使用函数。

fun 函数名称([函数参数...]): 返回值类型 {
    //函数体
}

函数需要完成我们的任务,在一定条件下可能需要返回结果,再赋值给变量或是参与运算等等,当然如果我们的函数只需要完成任务,不需要返回结果,返回值类型可以不填。

fun hello(): Unit {  
//Unit类型表示空,类似于Java中的void,默认情况下省略
    println("你好")
}

fun main() {
    hello()     //调用函数

需要外部传入一些参数的情况,并且需要返回结果的方法。

fun add(a: Int, b: Int): Int {
    return a + b
}

//假如只有一行,可以省略return,和前面的if等类似
fun add1(a: Int, b: Double) = a + b   //返回类型可自动推断,此处返回double

fun main() {
    val sum =add1(1, 2.toDouble())
    //toDouble也是函数,函数需要传入double而不是int,需要转换类型

    println(sum)
    println(add(5, 2))
    //带返回值的函数,调用之后得到的返回值,可以直接作为其他函数的参数
}

默认参数、命名参数、可变参数 vararg。

fun greet(name: String = "朋友", punctuation: String = "!") =
    //返回一个字符串(自动推断),参数可以提供默认
    "你好,$name$punctuation"

fun join(vararg parts: String, sep: String = ", ") =
    //vararg是一个关键字,用于声明可变参数, 允许函数接受任意数量的参数
    parts.joinToString(sep)

fun main() {
    println(greet())                          //你好,朋友!
    println(greet(punctuation = "~"))        //你好,朋友~
    println(greet(name = "小林"))             //你好,小林!
    println(join("A", "B", "C"))              //A, B, C
    println(join("AAA", 88.toString(), greet(), sep = " | ")) 
//AAA | 88 | 你好,朋友!

}

默认情况下,变量的获取就是直接返回,设置就是直接修改,不过有些时候我们可能希望修改这些变量获取或修改时执行的操作,我们可以这样。

var str: String = "你好"
    set(value) {  //只读属性没有
        println("懵了吧 $value")
        field = value
    }
    get() {
        return "$field,世界"
    }

//只读示例
val only: String = "只读"
    get() {
        println("只读属性")
        return field
    }

fun main() {
    println(str)
    str = "haha"
    println(str)
    println(only)
}

2.递归函数

标准的“递归求和”示例

fun main() {
    test(5)
}

//计算0-n的和的功能
fun test(n: Int): Int{
    if(n <= 0) return 0      //当n等于0的时候就不再向下,而是直接返回0
    return n + test(n - 1)   //n不为0就返回当前的n加上test参数n-1的和
}


//test(5)
//→ 5 + test(4)
//→ 4 + test(3)
//→ 3 + test(2)
//→ 2 + test(1)
//→ 1 + test(0)
//→ 0   // 基例:n <= 0

斐波那契数列是一个非常经典的数列,它的定义是:前两个数是1和1,之后的每个数都是前两个数的和。对于求解斐波那契数列第N个数这类问题,我们也可以使用递归来实现:

fun main() {
    println(fib(5))
}

fun fib(n: Int): Int{
    if(n <= 2) 1   //我们知道前两个一定是1,所以直接返回
    return fib(n - 1) + fib(n - 2)   
    //当前fib(n)的结果就是前两个结果之和,直接递归继续找
}

但是,这个有一个欠缺点,就是使用原始递归:时间复杂度 为O(2^n),指数级增长,太慢了,于是我们使用尾递归:时间复杂度降为了 O(n),空间复杂度也只为 O(1)

fun main() {
//    println(fib(5))
    // 递归,打印第五个斐波那契数
    println(fibTailrec(5))
}

tailrec fun fibTailrec(n: Int, a: Int = 1, b: Int = 1): Int {
    return if (n <= 2) b else fibTailrec(n - 1, b, a + b)
    //如果只有一行,则可以省略{},全部一行
}

3.使用函数库介绍

Kotlin为我们内置了大量实用的库函数,我们可以使用这些库函数来快速完成某些操作。

幂、绝对值、最大最小、平方根

import kotlin.math.* //导包

fun main() {
    2.0.pow(10.0)    // 1024.0
    abs(-1)          // 1
    max(19, 20)      // 20
    min(2, 4)        // 2
    sqrt(9.0)        // 3.0
}

4.高阶函数和lambda表达式

Kotlin中的函数属于一等公民,它支持很多高级特性,甚至可以被存储在变量中,可以作为参数传递给其他高阶函数并从中返回,就想使用普通变量一样。 为了实现这一特性,Kotlin作为一种静态类型的编程语言,使用了一系列函数类型来表示函数,并提供了一套特殊的语言结构,例如lambda表达式。

高阶函数就是“接收函数”或“返回函数”的函数,用来把变化的行为参数化,提升复用与可组合性。

//定义一个高阶函数,接受两个数字类型和一个函数作为参数
fun operate(a: Int, b: Double, f: (Int, Double) -> String): String = f(a, b)
// (Int, Double) -> Int,表示接受两个Int和Double参数并返回String的函数

fun main() {
    println(operate(3, 5.0) { x, y -> (x + y).toString() + "哈哈" }) //8
}

**解释:**函数类型用 (参数类型) -> 返回类型 表示,也能起别名让签名更清晰。typealias是 Kotlin中的一个关键字,用于创建类型别名。

//为函数类型创建别名
typealias NumberOperation = (Int, Double) -> String

//使用别名简化函数声明
fun operate(a: Int, b: Double, f: NumberOperation): String = f(a, b)

fun main() {
    println(operate(1,45.0) { a, b -> (a+b).toString() +"是答案"})
}

:: 是 Kotlin 中的成员引用操作符,用于引用类的成员函数或属性。

//定义接收函数参数的高阶函数
fun operate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}

fun main() {
    //使用math函数引用作为参数
    println(operate(3, 5, Int::plus))    //8
    println(operate(3, 5, Int::times))   //15
    println(operate(10, 3, Int::minus))  //7
}

上面函数改装。

//这个套娃了点
typealias NumberOperation = (Int, Double) -> String

fun addAndFormat(a: Int, b: Double): String = (a + b).toString() + "是答案"

fun operate(a: Int, b: Double, f: NumberOperation): String = f(a, b)

fun main() {
    println(operate(1, 45.0, ::addAndFormat))  //46.0是答案
}

除了引用现成的函数之外,我们也可以使用匿名函数,这是一种没有名称的函数。匿名函数除了没名字之外,其他的用法跟函数是一样的。

fun main() {
    val func: (String) -> Int = {
        println("这是传入的内容$it")  //只有一个参数时候,it代表传入本身
        666
        //和上节课说的一样,最后一个默认返回值,可以不写return
    }
    val result = func("哈哈")
    println(result)
}

ambda 本质上是“函数字面量”,写法是 {参数列表 -> 函数体}。类型可以显式写,也可依赖推断。

fun main() {
    val add: (Int, Int) -> Int = { x, y -> x + y }      
    //显式函数类型
    val add1: (Int, Int) -> Int = { _, y -> y * 2}      
    //忽略第一个参数,可以使用_代替
    val add2 = { x: Int, y: Int -> x + y }              //由参数类型推断返回类型
    val square: (Int) -> String = { "正方形面积为 " + it * it } //单一参数可用 it
    val greet: (String) -> Unit = { name -> println("你好, $name") }

    val op = { x: Int, y: Int ->
        val sum = x + y
        sum * 2  //
    //多行lambda的最后一行作为返回值
    }

    println(add(1, 2))      //3
    println(add1(1, 2))
    println(add2(1, 2))
    println(square(6))      //36
    greet("小林")
    println(op(1, 2))   //6
}

在Kotlin中,如果函数的最后一个形式参数是一个函数类型,可以直接写在括号后面,就像下面这样。

fun repeatTime(times: Int, action: (Int) -> Unit) {
    //传入整数类型,最后一个形参是 (Int) -> Unit,
    // 所以调用时可用尾随lambda
    for (i in 1..times)
        action(i)
}

fun main() {
    repeatTime(3) { i ->
        println("第 $i 次")
    }
}

5.内联函数

内联函数(inline)就是让编译器把函数体直接“展开”到调用处,常用于接收 lambda 的小工具函数,能少分配对象、少一层调用,也能在 lambda 里直接 return 外层函数。

内联函数虽换来性能上的提升,但建议在高阶函数使用,因为普通函数压根没有必要

fun main() {
    test { println("打印:$it") }
}

inline fun test(func: (String) -> Unit){
    println("内联函数")
    func("你好")
}


//编译后
fun main() {
    println("内联函数") 
  	val it = "你好"
    println("打印:$it")
}

当把一个 lambda 传给内联函数(inline)时,在这个 lambda 里写 return,会直接返回到外层函数,而不是只结束 lambda 自己。这就叫非局部返回。

inline fun check(flag: Boolean, block: () -> Unit) {
    println("check: before")   //第四步
    if (flag) block()   //第五步
    println("check: after")//不会执行
}

fun demoA() {
    println("demoA: start")  //第二步
    check(true) {   //第三步
        println("lambda: run")  //第六步
        return           //直接结束 demoA() 第七步
    }
    println("demoA: end")//不会执行
}

fun main() {
    demoA()   //第一步
}

//demoA: start
//check: before
//lambda: run

如果你不想跳出外层函数,只想结束 lambda,用标签返回即可:return@函数名。

inline fun check(flag: Boolean, block: () -> Unit) {
    println("check: before")
    if (flag) block()
    println("check: after")
}

fun demoB() {
    println("demoB: start")
    check(true) {
        println("lambda: run")
        return@check      //只结束 lambda,自身返回
    }
    println("demoB: end") //会执行
}

fun main() {
    demoB()
}

下一节,我们将要讲解类与对象,来看看如何去定义类,使用类。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值