kotlin杂谈系列九(有点水)
-
这节是来浅谈一下DSL的
-
什么是DSL : DSL一种特定领域的语言 是专门化的比起通用性语言效率更高
-
DSL分为内部DSL和外部DSL 外部DSL需要额外的解析器 而内部的DSL则依赖宿主语言的编译器
-
所以我们这里讲的是内部DSL 在kotlin中的应用
-
DSL就是借助了kotlin的 中缀,带接收方的lambda,和一些作用域函数,高阶函数来实现的,DSL的主要是依赖这些这些是设计DSL的基础 自己设计出来很惊喜但是要付出一定的努力 设计这个事不是语法细节不在我们讨论的事情中
-
可以通过注解来限制嵌套的lambda表达式只能访问直接接收方(隐式接收方)
-
@DslMarker 来注解一个注解
@DslMarker
annotation class XMLMarker
-
由于我的水平对注解只是了解过并不会深刻的用过 所以我决定在kotlin杂谈系列结束后深入的学习java
-
虽然注解可以限制父类的接收方 但是还是无法限制顶级函数 和词法作用域中的变量和属性
-
以上就是我对DSL的浅谈
-
函数递归尾调用(tailrec)
递归的思想 : 利用解决子问题的方案来解决问题的方案这时候就可以利用递归了
递归的技术虽然好但是他会在处理大型数据的时候发生栈溢出的异常
我们暂且先将编写的代码看做一个过程而字节码最终作为一个进程来运行
就例如 如果我们把递归的代码那么字节码就是在运行递归的过程
如果我们写的是迭代的代码 那么字节码就是运行迭代的过程 而尾调用就是把看起来 是递归的过程在字节码阶段变成迭代的过程
但是作为代价 他将失去返回值类型推导的能力
tailrec fun factorial(n:Int,result:BigInteger = 1.toBigInteger()):BigInteger =
if (n<=0) result else factorial(n-1,result*n.toBigInteger())
public final class RecMethodKt {
@NotNull
/*--------------------重点-----------------------*/
public static final BigInteger factorial(int n, @NotNull BigInteger result) {
while(true) {
if (n <= 0) {
return result;
}
int var10000 = n - 1;
BigInteger var10001 = BigInteger.valueOf((long)n);
BigInteger var3 = var10001;
var10001 = result.multiply(var3);
result = var10001;
n = var10000;
}
}
/*--------------------重点-----------------------*/
//省略了部分不必要的代码
}
- 通过反编译的代码可以看见我们的递归代码变成了迭代的代码
来看看非尾递归的代码
fun factorial(n:Int,result:BigInteger = 1.toBigInteger()):BigInteger =
if (n<=0) result else factorial(n-1,result*n.toBigInteger())
public final class RecMethodKt {
@NotNull
public static final BigInteger factorial(int n, @NotNull BigInteger result) {
BigInteger var10000;
if (n <= 0) {
var10000 = result;
} else {
int var4 = n - 1;
BigInteger var10001 = BigInteger.valueOf((long)n);
BigInteger var3 = var10001;
var10001 = result.multiply(var3);
var10000 = factorial(var4, var10001);//这里就是递归代码
}
return var10000;
}
//省略了部分不必要的代码
}
-
可以看见没有用tailrec的代码在字节码底层就是递归
-
注意尾调用的意思是 : 递归的函数必须在函数定义的尾部即最后否则是没有用的
-
记忆
就是将计算了的计算结果缓存起来避免重复计算
记忆只能用于纯函数即没有副作用的函数
kotlin虽然没有直接提供对记忆的支持 但是可以使用给函数注入函数 或者委托的方式来实现
fun fib(n : Int) : Long = when(n){
0,1 -> 1L
else -> fib(n-1) + fib(n-2)
}
fun main() {
println(measureTimeMillis { fib(40) })
println(measureTimeMillis { fib(45) })
}
//输出 : 342 3491
-
可以看到当数据较大的时候每增加一个数后时间会指数级增长
-
记忆
在递归过程中保存一些重复利用的数据来进行优化但是这部分很高级不是很清楚
- 一般有两种 一个是像函数里注入函数来进行记忆(但是我没有成功)
- 一种是委托一个函数就是一个对象 那么调用他的时候就相当于getValue()
所以看看代码吧
//委托类
class Memoize<T,R> (val func : (T)->R){
val cache = mutableMapOf<T,R>()
operator fun getValue (thisRef : Any?,property : KProperty<*>) = { n:T ->
cache.getOrPut(n){func(n)}
}
}
//委托的函数
val fib : (Int)->Long by Memoize{ n: Int ->
when (n) {
0, 1 -> 1L
else -> fib(n - 1) + fib(n - 2)
}
}
//使用
println(measureTimeMillis { fib(50) })
-
由于我没有成功函数注入函数来记忆所以这里就不演示了
-
我自己也没有很理解记忆这个算法优化等以后学了数据结构和算法来重新谈谈
-
感觉最近的文章有点水了 其实是我也是第一次接触这些东西有点不熟悉 所以这次杂谈就当成我的学习笔记吧