kotlin:精湛的递归

首先来看一段代码:

// kotlin

interface Expr

class Num(val value: Int) : Expr
class Sum(val left: Expr, val right: Expr) : Expr

fun eval(e: Expr): Int =
        when (e) {
            is Num -> {
                println("num: ${e.value}")
                e.value
            }
            is Sum -> {
                val left = eval(e.left)
                val right = eval(e.right)
                println("sum: $left + $right")
                left + right
            }
            else -> throw UnknownError("unknown err")
        }


fun main(args: Array<String>) {
    println(eval(Sum(Sum(Num(1), Num(2)), Sum(Num(4),Num(5)))))
}

这段代码来自 《Kotiln in Action

然后是看一下输出:

num: 1
num: 2
sum: 1 + 2
num: 4
num: 5
sum: 4 + 5
sum: 3 + 9
12

代码量很少,但是能看出这是一个递归。

但是看到 eval(Sum(Sum(Num(1), Num(2)), Sum(Num(4),Num(5)))) 这句调用,估计很多人都是一脸懵逼。

首先要明确一点, eval的参数是 Expr 所以,上面这段调用是符合语法规范的。

然后是分析一下 eval的实现逻辑:

  1. 如果参数是Num类型的,直接返回对应的 value,不进行递归;
  2. 如果参数是Sum类型的,分别对其left,right调用 eval(), 也就是递归,并且对返回值求和。

    由于 eval的返回值是 Int , 所以求和也没毛病。

看上面的 调用栈知道,如果参数不是Num, 就会一直递归,直到参数是Num类型为止。所以这样理论上不会导致栈溢出,因为有递归结束条件。

如果使用Java来写的话,可读性会好很多。

这个递归最巧妙的地方,我感觉是:
Sum需要两个参数,类型都是Expr , 如果希望传入的是Sum类型 ,那么这个作为参数的Sum 依然需要两个Expr,那即使你继续创建Sum,最终还是需要两个Expr ,而只有创建Num(Int), 才能完成一个Expr对象的实例化。
这就导致,你需要至少有两个Num类型的Expr对象,你才能完成一个Sum类型对象的实例化。
eval 就利用了这一点,保证了递归不会一直调用下去。因为总会碰到Num类型的参数,碰到了,就不会再递归,直接进行取值。

// java
public static int eval(Expr e) {
    if (e instanceof Num) {
        Num n = (Num) e;
        System.out.println("NUM: " + n.getValue());
        return n.getValue();
    } else if (e instanceof Sum) {
        Sum s = (Sum) e;
        int left = eval(s.getLeft());
        int right = eval(s.getRight());
        System.out.println("SUM: " + left + " + " + right);
        return left + right;
    }

    throw new RuntimeException("error when eval...");
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值