Kotlin(十七)函数式编程<1>

前言:函数式编程分为狭义和广义两个方面
狭义函数式编程,有着非常严格的标准,只通过纯函数编程,不允许有副作用,所有的数据结构都是不可以改变的。

广义的函数式编程,不强调的都是纯函数。常见的函数式的语言特性:

  • 函数是头等公民
  • 方便的闭包语法
  • 递归式的构造列表
  • 柯里化的函数
  • 惰性求值
  • 模式匹配
  • 尾递归优化
  • 强大的泛型能力,包括高阶类型(支持静态类型的函数式语言)
  • Typeclass
  • 类型推导。
    Kotlin 支持上面大部分特性。

纯函数和引用透明性

sealed class Format
data class Print(val text: String) : Format()
object Newline : Format()
val string = listOf(Print("Hello"),
    Newline, Print("Kotlin"))
fun unsafeInterpreter(str:List){
    str.forEach {
        when(it){
            is Print -> print(it.text)
            is Newline -> println()
        }
    }
}

unsafeInterpreter,引入了副作用

1.缺乏可测试性。如果变更,添加不是print而是写数据库相关方法,如果测试的时候,就会麻烦。

2.难易组合复用。内部混合了字符串格式转化逻辑,如果这里不是打印操作,而是数据库操作,就不能当做字符串转换功能使用。

稍微改动一下,举个例子说明:

fun stringInterpreter(str:List)
        = str.fold(""){fullText,s ->
    when(s){
        is Print -> fullText + s.text
        is Newline ->fullText + "\n"
    }
}

函数返回字符串结果,无论测试还是复用,都有所提升

基本法则:引用透明性
一个表达式在程序中,可以被它等价的值替换,而不影响结果

一个函数具备引用透明性,内部行为不会改变外部状态

近似数学中的等式推理

纯函数与局部可变性

引用透明不是说不可变,而是局部可变,而整体调用外面是个黑盒,结果是一致的。

fun foo(x:Int):Int{
    var y = 0
    y = y + x
    return y
}

foo具备纯函数f(x)->y.

副作用,在一定的抽象概念,并不绝对没有副作用,即使纯函数,也会使用内存,占用cpu

纯函数具有局限性random随机函数结果不一致都是不同的,不是纯函数

代换模型与惰性求值

错误的例子StackOverflowError 死循环

fun f1(x:Int,y:Int) = x
fun f2(x:Int):Int = f2(x)
  1. 应用序和正则序

kotlin中会先对f2(2) 进行求值,所以会导致死循环
然而如Hashell这种老的纯函数语言,会先调用f1(1,y),因为用不到y所以不会对f2(2)求值

  1. 惰性求值
    模拟一下
    比如针对println,他是一个非纯函数,改造让他"lazy"
fun lazyPrintln(msg:String) = { println(msg)}

当我们执行 lazyPrintln("I am IO operation"),
他仅仅返回一个可执行的println是惰性的,也是可以替代的。

你可能感兴趣的:(Kotlin(十七)函数式编程<1>)