#递归 #变成 #函数式 #跳板

tramp

支持互递归的递归函数跳板

4个版本 (2个重大更改)

使用旧Rust 2015

0.3.0 2018年6月16日
0.2.1 2018年6月16日
0.2.0 2018年6月16日
0.1.0 2018年6月9日

#18 in #变成

Download history 23/week @ 2024-03-12 27/week @ 2024-03-19 12/week @ 2024-03-26 39/week @ 2024-04-02 15/week @ 2024-04-09 19/week @ 2024-04-16 35/week @ 2024-04-23 19/week @ 2024-04-30 19/week @ 2024-05-07 21/week @ 2024-05-14 18/week @ 2024-05-21 30/week @ 2024-05-28 15/week @ 2024-06-04 19/week @ 2024-06-11 20/week @ 2024-06-18 20/week @ 2024-06-25

每月77次下载

MIT许可证

8KB
58

此crate为Rust中的递归提供了某些功能。

动机

大多数迭代问题都可以用递归而不是循环来解决。事实上,当你开始学习函数式编程时,你会发现使用递归解决给定问题的可能性。如果语言不优化某些类型的递归,编程函数式可能很困难,因为如果发生大的递归情况,最终可能会发生栈溢出。

一种常见的优化是所谓的“尾调用优化”、“尾调用归约”或任何类似的名称。首先,让我们了解尾调用。尾调用基本上是在函数体“尾部”找到的函数。尾位置意味着调用是函数体中最后完成的事情。优化它意味着,编译器不会为被调用函数生成新的堆栈帧,而是将重用当前帧。实际上,调用可能被转换成一个循环。但是,它仍然是递归,因为程序员没有编写循环。这只是优化。

目前,Rust没有提供任何语言项来优化尾调用。有一个“承诺”的特性名为becomebecome目前只是一个保留的标识符,但意图是它像一个返回一样,但是执行尾调用归约。好吧,目前还没有关于何时实现或是否将实现它的预期。因此,我们决定创建一个库,允许程序员编写带有尾调用的优化递归。

示例

看看这个阶乘函数

fn fac(n: u128) -> u128 {
    if n > 1 {
        n * fac(n - 1)
    } else {
        n
    }
}

很明显,上面的函数不是尾递归。在递归调用之后,我们仍然需要将结果乘以n。然而,这个函数有一个众所周知的版本,它接受一个额外的参数:累加器。看看

fn fac(n: u128) -> u128 {
    //!
    fn fac_with_acc(n: u128, acc: u128) -> u128 {
        if n > 1 {
            fac_with_acc(n - 1, acc * n)
        } else {
            1
        }
    }

    fac_with_acc(n, 1)
}

fac_with_acc函数是尾递归的。在调用自己之后没有其他工作要做。只有一个问题:Rust还没有进行任何尾调用优化。现在,crate tramp完成了它的任务。我们在该crate中实现了一个跳板。跳板通过调用一个可能返回另一个函数(将被再次调用)或计算结果的函数来模拟尾调用优化。跳板将循环调用返回的函数,每当函数返回一个计算值而不是一个函数时,跳板返回该值。请看下面的示例。

#[macro_use] extern crate tramp;

use tramp::{tramp, Rec};

fn factorial(n: u128) -> u128 {
    fn fac_with_acc(n: u128, acc: u128) -> Rec<u128> {
        if n > 1 {
            rec_call!(fac_with_acc(n - 1, acc * n))
        } else {
            rec_ret!(acc)
        }
    }

    tramp(fac_with_acc(n, 1))
}

assert_eq!(factorial(5), 120);

没有运行时依赖