当前位置: 首页 > 面试题库 >

Swift是否实现尾部调用优化?在相互递归的情况下?

张炳
2023-03-14
问题内容

特别是如果我有以下代码:

func sum(n: Int, acc: Int) -> Int {
  if n == 0 { return acc }
  else { return sum(n - 1, acc + n) }
}

Swift编译器会将其优化为循环吗?在下面更有趣的情况下会这样吗?

func isOdd(n: Int) -> Bool {
  if n == 0 { return false; }
  else { return isEven(n - 1) }
}

func isEven(n: Int) -> Bool {
  if n == 0 { return true }
  else { return isOdd(n - 1) }
}

问题答案:

最好的检查方法是检查编译器生成的汇编语言代码。我将上面的代码编译为:

swift -O3 -S tco.swift >tco.asm

输出的相关部分

.globl    __TF3tco3sumFTSiSi_Si
    .align    4, 0x90
__TF3tco3sumFTSiSi_Si:
    pushq    %rbp
    movq    %rsp, %rbp
    testq    %rdi, %rdi
    je    LBB0_4
    .align    4, 0x90
LBB0_1:
    movq    %rdi, %rax
    decq    %rax
    jo    LBB0_5
    addq    %rdi, %rsi
    jo    LBB0_5
    testq    %rax, %rax
    movq    %rax, %rdi
    jne    LBB0_1
LBB0_4:
    movq    %rsi, %rax
    popq    %rbp
    retq
LBB0_5:
    ud2

    .globl    __TF3tco5isOddFSiSb
    .align    4, 0x90
__TF3tco5isOddFSiSb:
    pushq    %rbp
    movq    %rsp, %rbp
    testq    %rdi, %rdi
    je    LBB1_1
    decq    %rdi
    jo    LBB1_9
    movb    $1, %al
LBB1_5:
    testq    %rdi, %rdi
    je    LBB1_2
    decq    %rdi
    jo    LBB1_9
    testq    %rdi, %rdi
    je    LBB1_1
    decq    %rdi
    jno    LBB1_5
LBB1_9:
    ud2
LBB1_1:
    xorl    %eax, %eax
LBB1_2:
    popq    %rbp
    retq

    .globl    __TF3tco6isEvenFSiSb
    .align    4, 0x90
__TF3tco6isEvenFSiSb:
    pushq    %rbp
    movq    %rsp, %rbp
    movb    $1, %al
LBB2_1:
    testq    %rdi, %rdi
    je    LBB2_5
    decq    %rdi
    jo    LBB2_7
    testq    %rdi, %rdi
    je    LBB2_4
    decq    %rdi
    jno    LBB2_1
LBB2_7:
    ud2
LBB2_4:
    xorl    %eax, %eax
LBB2_5:
    popq    %rbp
    retq

生成的代码中没有任何呼叫说明,只有条件跳转(je/ jne/ jo/ jno)。显然,这表明Swift确实在 两种
情况下都进行了尾部调用优化。

此外,isOdd/ isEven函数很有趣,因为编译器不仅似乎在执行TCO,而且还在每种情况下内联其他函数。



 类似资料:
  • 我有以下代码失败,错误如下: RuntimeError:超出最大递归深度 我试图重写它以允许尾部递归优化(TCO)。我相信,如果发生了TCO,那么这段代码应该是成功的。 我应该得出结论,Python不做任何类型的TCO,还是我只需要以不同的方式定义它?

  • 问题内容: 我有以下代码失败,并出现以下错误: 超过最大递归深度 我试图重写此代码以允许尾递归优化(TCO)。我相信,如果发生了TCO,则该代码应该会成功。 我是否应该得出结论,Python不执行任何类型的TCO,还是只需要以不同的方式定义它? 问题答案: 你可以通过这样的转换来手动消除递归

  • 假设我编写这样的代码: 我如何让Kotlin优化这些相互递归的函数,以便在不抛出StackOverflower错误的情况下运行main?tailrec关键字适用于单函数递归,但没有更复杂的功能。我还看到一个警告,在使用关键字tailrec的地方没有找到尾部调用。也许这对编译器来说太难了?

  • 我想我理解了教科书中对尾部递归函数的定义:在函数调用后不执行任何计算的函数。我还发现,作为一个结果,尾部递归函数的内存效率会更高,因为它每次调用只需要一条记录,而不是每次都需要保留一条记录(就像在普通递归中那样)。 我不太清楚的是,这个定义如何应用于嵌套调用。我将提供一个例子: 我最初给出的答案是,根据定义,它不是尾部递归的(因为外部调用是在计算内部调用之后执行的,所以其他计算是在第一次调用之后完

  • 问题内容: 我尝试在网络上进行挖掘以使问题得到解答。我找到了一些与达芬奇计划有关的文件。这被标记为与在JVM中包含闭包有关的JSR 292。这个项目实现了吗,它是Java 8的一部分吗? 问题答案: 据我所知,Java 8没有尾调用优化。Afaik与实际的编译器技巧无关,因为它很简单,但是为了安全起见保留了一个调用栈。但是我想使用字节码重写器是可能的。

  • 本文向大家介绍详解JavaScript调用栈、尾递归和手动优化,包括了详解JavaScript调用栈、尾递归和手动优化的使用技巧和注意事项,需要的朋友参考一下 调用栈(Call Stack) 调用栈(Call Stack)是一个基本的计算机概念,这里引入一个概念:栈帧。 栈帧是指为一个函数调用单独分配的那部分栈空间。 当运行的程序从当前函数调用另外一个函数时,就会为下一个函数建立一个新的栈帧,并且