危险气息的研究室:尾递回 Tail Calls

研究生和大学生不同,跟着指导的教授有着独立的研究室,以滞留时间来看,可说是研究生的第二个家。

「呐,小唯心,最近的学生是不是太死背考古题了啊。」某个教授闲来没事,和自己的研究生发牢骚。

「谁叫教授们为求省事,使用题库呢,要不然这次改出新题?」唯心一边整理数据,一边淡淡回应。

「咦?麻烦?只要确认学生能按要求写出合格的程序就好了吧。」翁玉慵懒地玩弄自己的头发。

「恕我直言,教授讲解的尾递回也不知道有多少学生听懂,说不定连根本的递回也没弄清楚。」

翁玉疑惑地转过头。「不是吧?递回就是函式在执行过程里又呼叫自己,这个也不懂的话,现在的学生真的没问题吗?」

「教授你忘记这次的选课开放新生和外系了吗?」唯心善意提醒。

「难怪这次教室里的女孩子比以往多。那为了让他们明白,也许放些实际例子会比较容易理解吧。现在的高中数学有哪些可以用的例子?」

唯心回想了一下,答道:「费氏数列和阶乘吧?」

「那就选阶乘好了,费氏数列要呼叫自己两次,还要数位置,比较难懂吧?」翁玉戳着唯心养的仙人掌,仙人掌的软刺触感甚得她心。

fun 费氏数列(n: Int): Int {
    return if (n < 2) { n }
    else 费氏数列(n - 1) + 费氏数列(n - 2)
}

唯心迅速救走仙人掌盆栽。「也因为呼叫多次,改写成尾递回的优势才明显啊。毁灭性的Stack Overflow Error不就是因为递回呼叫产生的堆叠太多,还没回收回来就爆掉了。」唯心顿了一下。「说起来堆叠是在资料结构课程的范围,如果教授你要推广尾递回,那就要让他们认识堆叠了。」

「要从那里开始吗⋯⋯」翁玉无语。

唯心联想到她和诗忆的补课方式,於是提议:「或许可以说的笼统一点,比如说,每次呼叫函式都会占据一单位记忆体,当函式执行完才能回收记忆体,所以在执行期间呼叫多个自己的递回就会占据大量记忆体,最後记忆体满了程序就挂了。尾递回算是先和程序约好每次最多只能呼叫一次自己,也不能用到其他资源,所以程序能准备重复利用记忆体的机制。」

fun 阶乘(n: Int): Int {
    return if (n < 2) { n }
    else n * 阶乘(n - 1)//因为还带了 n * 所以只是普通的递回
}

tailrec fun 阶乘尾递回(n: Int, tail: Int = 1): Int {
    return if (n < 2) { tail }
    else 阶乘尾递回(n - 1, n * tail)
}

「是呀,也不是所有程序语言都能支援尾递回,Java8就不行。说起来小唯心怎麽知道有同学不懂尾递回?」翁玉突然想到这个问题。

「因为有不少同学拿着程序码来问我他写的是不是尾递回呀,呵呵。」对面传来有点阴森的笑声。「我和他们说,在函式前面加上tailrec执行看看,IDE发现不对劲就会发警告。」

「嗯,很好的方法。对了,像之前一样让学生来这里找你也可以唷。」翁玉话才说完,感觉室内温度立即下降了几度。「⋯⋯我有事先走了,下次见唷小唯心。」不能再待在这间越发散发危险气息的研究室哩。


<<:  初探 Vaadin on Kotlin - day03

>>:  Day 3 - A short introduction to gcc usage - 2

成员 25 人:如何安稳地抽走猫躺的软垫

「好企业没有舒适圈;  如果你觉得今天很舒适,也撑不过明天。」 新创公司,总能看清人生百态。 你知道...

Day3 - numpy(2) 基本索引

今天的重点 索引 基本索引: 先建立一个4x3的ndarray来让我们实际操作 阵列索引是由外而内的...

Day18 Gin with GORM

What is ORM ORM全名为Object-Relational Mapping 物件关系对应...

IF EXISTS (SELECT * FROM table where 1=2)

--哇.这是什麽资料库,我怎麽没这个语法. drop table if exists delme c...

ASP.NET MVC 从入门到放弃(Day25)-MVC模型验证介绍

接下来讲讲 Model 验证规则部分... 在 模型类别上方需加入 using System.Com...