目录
如果你要理解递归,首先你要理解递归.
介绍
我尝试写些关于编程语言的东西. 目前刚开始写,还把握不住严谨(数学上的)和易懂(语言上的)的边界. 我会不断润色,找到那个度. 你有问题就可以给我说.
递归是程序设计中的重要部分, 比如解析文件. 但是
一般的文章或者书籍都是通过介绍和例子来引入概念,没有做到知其所以然.
本文尝试通过引入编程语言的设计和实现来帮助理解递归.
概述
计算机是如何实现函数调用的? 一句话就是保存当前信息, 然后跳入函数中执行, 最后再从此函数返回. 高级的编程语言无非就是在模拟汇编语言执行函数调用的过程. 只是中间这个过程是通过编译器自动转换了.
那问题来了,函数可不可以调用自身?当然可以, 一般情况下,进入新的函数,会在内存中开辟一个新的栈帧来存放数据. 如果调用过深,那么进程的地址空间就不够存这些栈帧了,那么就会栈溢出.
我们今天主要来研究编程语言是怎么实现函数调用,进而理解递归.
编程语言如何实现普通函数调用
为了更好地展示,我们用个Python的例子来解释.例子就是递归打印1-10.
def recursive_print(n):
if n == 1:
print(n)
else:
recursive_print(n-1)
print(n)
recursive_print(10)
发生函数调用时, 进程会保存当前上下文,并创建栈来保存函数的变量.假设当前函数调用是recursive_print(5), 当前栈保存有{n=5}. 你即将进入函数recursive_print(4), 此时创建一个新的栈并存有数据{n=4}.
由此可以看到, 什么递归不递归的,只要是函数调用,只要把相关变量信息保存下来进行函数调用.计算机才不在乎.
编程语言如何实现带返回值的函数调用
如果你理解普通函数调用,带返回值的无非就是约定个地方,然后父函数从这个位置把值取到.
我随便找个x86 CPU的说明:
The caller can expect to find the return value of the subroutine in the register RAX.
给个例子:
def factorial(n):
if n == 1:
return 1
else:
return n*factorial(n-1)
print(factorial(10))
递归
如果理解上述问题,递归就可以理解为普通函数调用,只是每次调用传入的变量的值可能不一样.
计算机很傻,你给他相关的信息就行,他不关系其他的.
课后作业
- 你如何理解二叉树上的递归? 或者说现在可以理解吗?
- Haskell语言不存在while语句,如果有兴趣可以尝试用haskell编写上述两个例子.你会更好理解递归.
- 如果让你创造一门语言,比如mocca语言(最近疯狂喝这个),你会怎么模拟函数调用?用到哪些数据结构?