首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

在Scala中使用尾部调用递归获取子问题的结果

在Scala中,尾部调用递归是一种优化技术,它允许函数在递归调用时不会增加额外的栈空间消耗。通过使用尾部调用递归,可以避免栈溢出的风险,提高代码的性能和可读性。

尾部调用递归是指在函数的最后一步调用自身,并且没有其他操作需要执行。这样,编译器可以优化递归调用,将其转化为循环,从而避免栈的不断增长。

下面是一个使用尾部调用递归获取子问题结果的示例代码:

代码语言:txt
复制
def factorial(n: Int): Int = {
  @annotation.tailrec
  def loop(n: Int, acc: Int): Int = {
    if (n <= 0) acc
    else loop(n - 1, acc * n)
  }

  loop(n, 1)
}

val result = factorial(5)
println(result) // 输出 120

在上述代码中,factorial 函数使用尾部调用递归计算阶乘。内部的 loop 函数接收两个参数 nacc,其中 n 表示当前的数字,acc 表示累积的结果。如果 n 小于等于 0,则返回累积的结果 acc。否则,将 n 减 1,并将 acc 乘以 n,然后递归调用 loop 函数。

在这个示例中,尾部调用递归确保每次递归调用都是在函数的最后一步执行,并且没有其他操作需要执行。这使得编译器能够对递归调用进行优化,将其转化为循环,从而避免栈溢出的风险。

推荐的腾讯云相关产品:无

参考链接:

页面内容是否对你有帮助?
有帮助
没帮助

相关·内容

Scala 【 4 参数、过程以及数组 Array 和 ArrayBuffer 】

过程、lazy值和异常 过程: scala ,定义函数时,如果函数体直接包括花括号里面,而没有使用 = 连接,则函数返回值类型就是 Unit 。这种函数被称为过程。...这种特性对于特别耗时计算操作特别有用,比如打开文件进行读写,后者进行网络 IO 。 定义时候不会被进行计算,有点像操作系统假分配,只有使用时候才会去进行计算,将结果返回。...Array、ArrayBuffer 以及遍历 Array scala Array 代表含义与 Java 类似,也是长度不可改变数组。...由于 Scala 与 Java 都是运行在 JVM ,双方可以互相调用,因此 Scala 数组底层实际上是 Java 数组。...使用 ++= 操作符,可以添加其他集合所有元素。 使用 trimEnd() 函数,可以从尾部截断指定个数元素。 使用 insert() 函数可以指定位置插入元素。

36830

大数据技术之_16_Scala学习_13_Scala语言数据结构和算法_Scala学习之旅收官之作

19.6.3 栈几个经典应用场景   1、子程序调用跳往子程序前,会先将下个指令地址存到堆栈,直到子程序执行完后再将地址取出,以回到原来程序。   ...2、处理递归调用:和子程序调用类似,只是除了储存下一个指令地址外,也将参数、区域变量等数据存入堆栈。   3、表达式转换与求值(实际解决)。   4、二叉树遍历。   ...: 表达式: 7*2*2-5+1-5+3-4 = 18 19.7 递归 recursive 19.7.1 看个实际应用场景 迷宫问题(回溯) 19.7.2 递归概念 简单说:递归就是函数/方法自己调用自己...,每次调用时传入不同变量,递归有助于编程者解决复杂问题,同时可以让代码变得简洁。...19.7.3 递归快速入门 我列举两个小案例,来帮助大家理解递归递归讲函数时已经讲过(当时讲相对比较简单),这里在给大家回顾一下递归调用机制   1、打印问题   2、阶乘问题 思路分析   if

1.5K10

Scala学习教程笔记一之基础语法,条件控制,循环控制,函数,数组,集合

注意,scala没有提供++,--操作,我们只可以使用+=和-=操作符; 7:apply函数:scalaapply函数是非常特殊一种函数,Scalaobject,可以声明apply函数。...,只要右侧函数体不包含递归语句,Scala就可以根据自己右侧表达式推断出返回类型。...3:默认参数,Scala,有时候调用某些函数时候,不希望给出参数具体指,而希望使用参数自身默认值,此时就在定义函数时使用默认参数。如果给出参数不够,则会从左往右依次应用参数。...异常:scala,异常处理和捕获机制与Java类似。...与Java都是运行在Jvm,双方可以互相调用,因此,Scala数组底层实际上就是Java数组。

1.4K50

递归算法

可能也有一大部分人知道递归,也能看递归,但在实际做题过程,却不知道怎么使用。今天,我们就来说一说递归算法使用。 什么是递归 递归,在数学与计算机科学,是指在函数定义中使用函数自身方法。...也就是说,递归算法是一种直接或者间接调用自身函数或者方法算法。 通俗来说,递归算法实质是把问题分解成规模缩小同类问题问题,然后递归调用方法来表示问题解。...递归使用 递归强大之处在于它允许用户用有限语句描述无限对象。因此,计算机科学递归可以被用来描述无限步运算,尽管描述运算程序是有限。 这一点是循环不太容易做到。...递归优化方法 递归问题中想到思路本身不非常难,真正难点在于如何优化。 1、考虑是否重复计算 如果你使用递归时候不进行优化,是有非常非常非常多问题被重复计算。...顾名思义,尾递归就是从最后开始计算, 每递归一次就算出相应结果, 也就是说, 函数调用出现在调用者函数尾部, 因为是尾部, 所以根本没有必要去保存任何局部变量。

56421

大数据--scala学习第一章:基础第二章:控制结构和函数第三章:数组第四章:字典和元组第五章:类第六章:对象第七章:包和引入第八章:继承第九章文件和正则表达式第十章特质:接口第十一章操作符第十二章函

可以通过追加:_*来解决不能接受问题:sum(2 to 10:_*) 14、过程:没有函数名后面等号函数称为过程,返回是Unit. 15、懒加载:lazy val words=初始化表达式 ,该变量只有使用时才会调用初始化...5、Scala中程序必须从object对象main方法开始。 第七章:包和引入 1、包和Java包类似,只是Scala定义包方式更多,可以使用{},可以文件顶部标记。...2、Scala作用域更加前后一致,包可以直接使用父包内容。 3、Scala引入了包对象,包对象可以定义方法,属性。...3、特质可以有具体实现方法,java接口Scala可以当做特质来使用,也可以new对象时继承特质:val acct=new Peolpe with Logger。...存在链表操作符::用于将两个链表合成新链表如:9::List(4,2) 结果是List(9,4,2),head为9.遍历时可以用迭代器进行遍历也可以直接通过递归

4.4K20

Spark Sql 源码剖析(二): TreeNode

零、前置知识 Scala Product trait // 所有 products 基trait,至少包含 [[scala.Product1]] 至 [[scala.Product22]] 及 [[scala.Tuple1..., visit 每个节点时候都会使用,记录当前 parse 节点是哪行哪列 另外,从 value 是 ThreadLocal 类型可以看出, Spark SQL ,parse sql 时都是单独...def foreach(f: BaseType => Unit): Unit = { f(this) children.foreach(_.foreach(f)) } 将函数 f 递归应用于节点及其节点...(_.transformDown(rule)) } else { // 如果应用了 rule 后节点有变化,则本节点换成变化后节点(children 不变),再将 rule 递归应用于节点...(非递归,一般将递归操作放在调用该函数地方)后该节点 copy。

90130

记一道字节跳动算法面试题

temp指向剩余链表,可以说是原问题一个问题。我们可以调用reverseKNode()方法将temp指向链表每K个节点之间进行逆序。...再调用reverse()方法把head指向那3个节点进行逆序,结果如下: ? 再次声明,如果对这个递归看不大懂,建议看下我那篇递归文章 接着,我们只需要把这两部分给连接起来就可以了。...而面试时候,经常会进行变形,例如这道字节跳动题,它变成从尾部开始组起,可能你一时之间就不知道该怎么弄了。当然,可能有人一下就反应出来,把他秒杀了。...我们把它从尾部开始组起,每 K 个节点为一组进行逆序。步骤如下 1、先进行逆序 ? 逆序之后就可以把问题转化为从头部开始组起,每 K 个节点为一组进行逆序。 2、处理后结果如下 ?...3、接着结果逆序一次,结果如下 ?

1.6K20

记一道算法面试题

temp指向剩余链表,可以说是原问题一个问题。我们可以调用reverseKNode()方法将temp指向链表每K个节点之间进行逆序。...再调用reverse()方法把head指向那3个节点进行逆序,结果如下: ? 再次声明,如果对这个递归看不大懂,建议看下我那篇递归文章 接着,我们只需要把这两部分给连接起来就可以了。...而面试时候,经常会进行变形,例如这道字节跳动题,它变成从尾部开始组起,可能你一时之间就不知道该怎么弄了。当然,可能有人一下就反应出来,把他秒杀了。...我们把它从尾部开始组起,每 K 个节点为一组进行逆序。步骤如下: 1、先进行逆序 ? 逆序之后就可以把问题转化为从头部开始组起,每 K 个节点为一组进行逆序。 2、处理后结果如下 ?...3、接着结果逆序一次,结果如下 ?

54910

排序7:归并排序

目录 1.排序思想 2.图解 3.递归版本 3.1排序代码实现 3.2 剩下主体部分 4.非递归版本 5.特性总结 ---- 1.排序思想 归并排序(MERGE-SORT)是建立归并操作上一种有效排序算法...我们肯定是要开额外空间来存储,然后每次将排序结果拷贝回原数组。 合并:分到最小排序之后就要合并了,合并之后再进行排序,每次排序完要把排序结果拷贝回原数组。...没有什么技术含量,我们只需要开辟好空间然后传参调用排序即可。...修正第一组尾部: 修正第二组全部: 修正第二组尾部: 考虑完了越界问题,才能够高枕无忧排序,非递归排序和递归思路一样。这里就不过多叙述。...归并缺点在于需要 O(N) 空间复杂度,归并排序思考更多是解决磁盘外排序问题。 2. 时间复杂度: O(N*logN) 3.

29330

记一道字节跳动算法面试题

temp指向剩余链表,可以说是原问题一个问题。我们可以调用reverseKNode()方法将temp指向链表每K个节点之间进行逆序。...再调用reverse()方法把head指向那3个节点进行逆序,结果如下: ? 再次声明,如果对这个递归看不大懂,建议看下我那篇递归文章 接着,我们只需要把这两部分给连接起来就可以了。...而面试时候,经常会进行变形,例如这道字节跳动题,它变成从尾部开始组起,可能你一时之间就不知道该怎么弄了。当然,可能有人一下就反应出来,把他秒杀了。...我们把它从尾部开始组起,每 K 个节点为一组进行逆序。步骤如下 1、先进行逆序 ? 逆序之后就可以把问题转化为从头部开始组起,每 K 个节点为一组进行逆序。 2、处理后结果如下 ?...3、接着结果逆序一次,结果如下 ?

71910

大数据技术之_16_Scala学习_04_函数式编程-基础+面向对象编程-基础

1、 scala ,方法和函数几乎可以等同(比如他们定义、使用、运行机制都一样),只是函数使用方式更加灵活多样。   ...12、递归函数未执行之前是无法推断出来结果类型,使用时必须有明确返回值类型。... scala ,可以使用 throws 注释来声明异常。...*   * 2、修改上一个程序,编写一个方法,方法不需要参数,计算该矩形面积,并将其作为方法返回值。main方法调用该方法,接收返回面积值并打印(结果保留小数点2位)。   ...* 另一个DogCaseTest类main方法,创建Dog对象,并访问say方法和所有属性,将调用结果打印输出。

2.1K10

快速学习-Scala函数式编程

Scala函数式编程 函数式编程基础 函数定义/声明 函数运行机制 递归//难点 [最短路径,邮差问题,迷宫问题, 回溯] 过程 惰性函数和异常 函数式编程高级 值函数(函数字面量) 高阶函数 闭包 应用函数...柯里化函数,抽象控制… scala,函数式编程和面向对象编程融合在一起,学习函数式编程式需要oop知识,同样学习oop需要函数式编程基础。...在学习Scala中将方法、函数、函数式编程和面向对象编程明确一下: scala,方法和函数几乎可以等同(比如他们定义、使用、运行机制都一样),只是函数使用方式更加灵活多样。...比如: Scala当中,函数是一等公民,像变量一样,既可以作为函数参数使用,也可以将函数赋值给一个变量....,函数创建不用依赖于类或者对象,而在Java当中,函数创建则要依赖于类、抽象类或者接口. 面向对象编程是以对象为基础编程方式。 scala函数式编程和面向对象编程融合在一起了 。

91210

递归递归之书:引言到第四章

文件夹搜索特定文件名是一个递归问题:您搜索文件夹,然后递归搜索文件夹文件夹。没有文件夹文件夹是导致递归搜索停止基本情况。...第五章,我们将研究使用分而治之策略递归求和函数,第八章,我们将研究使用调用优化递归函数。这些替代递归方法解决了本章求和函数一些问题。...遍历树图任务与许多递归算法紧密相关,例如本章解迷宫算法和第十一章迷宫生成程序。我们将研究树遍历算法,并使用它们来树数据结构查找特定名称。我们还将使用树遍历算法来获取树中最深节点算法。...例如,给定图 4-1 根节点,我们可以调用getDepth()并将其传递给根节点(A节点)。这将返回其节点B和C节点深度,再加一。函数必须递归调用getDepth()来获取这些信息。...递归函数调用类似于遍历到节点,而从递归函数调用返回类似于回溯到以前父节点。 虽然递归简单编程问题中被滥用,但它非常适合涉及类似树结构和回溯问题

58410

泛函编程(29)-泛函实用结构:Trampoline-不再怕StackOverflow

泛函编程方式其中一个特点就是普遍地使用递归算法,而且有些地方还无法避免使用递归算法。...虽然递归算法能使代码更简洁易明,但同时又以占用堆栈(stack)方式运作。堆栈是软件程序有限资源,所以使用递归算法对大型数据源进行运算时系统往往会出现StackOverflow错误。...如果不想办法解决递归算法带来StackOverflow问题,泛函编程模式也就失去了实际应用意义了。...针对StackOverflow问题Scala compiler能够对某些特别的递归算法模式进行优化:把递归算法转换成while语句运算,但只限于尾递归模式(TCE, Tail Call Elimination...但在实际编程,统统把递归算法编写成尾递归是不现实。有些复杂些算法是无法用尾递归方式来实现,加上JVM实现TCE能力有局限性,只能对本地(Local)尾递归进行优化。

1.7K101

scala 容器详细解释

操作类型是Elem => U,其中Elem是容器(collection)中元素类型,U是一个任意返回值类型。对f调用仅仅是容器遍历副作用,实际上所有函数f计算结果都被foreach抛弃了。...等容器类型已经与所需类型相匹配时候,所有这些转换器都会不加改变返回该容器。例如,对一个list使用toList,返回结果就是list本身。...比较(startsWith, endsWith, contains, containsSlice, corresponds)用于对两个序列进行比较,或者序列查找某个元素。...它们都是根据主键获取对应值映射操作。例如:def get(key): Option[Value]。“m get key” 返回m是否用包含了key值。...例如,我们可以像下述代码那样HashMap混入SynchronizedMap。 具体不可变集实体类 List 列表List是一种有限不可变序列式。

1.2K10

SparkSql优化器-Catalyst

模式匹配是许多函数编程语言特征,允许从代数数据类型潜在嵌套结构中提取值。Catalyst,语法树提供了一种转换方法,可以所有节点上递归地应用模式匹配函数,将匹配到节点转换为特定结果。...规则(和Scala模式匹配一般)可以匹配相同转换调用多个模式,使其非常简洁,可以一次实现多个转换: tree.transform { case Add(Literal(c1), Literal(c2...每个批次后,开发人员还可以新树上进行合理检查(例如,看看是否所有属性都是分配类型了),通常也通过递归匹配来编写。 最后,规则条件及其本身可以包含任意Scala代码。...2),将命名属性(如“col”)映射到给定操作符节点输入。...最后,将代码生成评估与对我们还没有生成代码表达式解释性评估结合起来是很明智,因为我们编译Scala代码可以直接调用到我们表达式解释器。 Catalyst代码生成器总共700行代码。

2.7K90

二刷二叉树,你也可以总结这些!

后序迭代遍历就是用栈实现,栈更像是“递归函数”细节过程,在用递归遍历时,我们甚至只想当前节点如何操作就行。...A函数调用B,A参数可能会在B函数在运行时发生改变,为了保持A函数调用B之前和之后一致性,必须在B函数运行完之后,将该参数重置到调用B之前状态,这样B运行完出调用栈之后,A函数还能基于之前运算结果继续运行...而指针先走到链表尾部,从尾部开始向头部逐个前移和后序遍历相似。 前序:顺序解决问题。先将当前节点操作完,再处理节点,等到节点都处理完了,问题也就解决了。...后序:当前节点要做事情需要借助“左右子树”计算结果,恰好后序就是等到左右子树递归函数完成后,可以记录他们递归函数返回值,用于当前结点操作。 例如计算二叉树结点,重复子树判断,公共祖先。...---- 我自己看了他总结,发现了小小问题,我知识星球里也指了出来。

34620

大数据分析工程师面试集锦2-Scala

11 方法和函数区别? 方法是定义函数,这个类进行实例化后会有一个同名方法,一般调用方法做法是使用缀点记法-实例名.方法名(参数……) 12 什么是偏函数?...正常递归,每一次递归操作,需要保存信息到堆栈,当递归步骤达到一定量时候,就可能会导致内存溢出,而尾递归,就是为了解决这样问题递归中所有的计算都是递归之前调用,也就是说递归一次计算一次,编译器可以利用这个属性避免堆栈错误...,尾递归调用可以使信息不插入堆栈,从而优化尾递归。...Scala工程抽象类和特质是很有用工具,这个问题需要先回答什么是抽象类以及什么是特质。...当调用该函数或方法时,如果没有传该参数值,Scala会尝试变量作用域中找到一个与指定类型相匹配使用implicit修饰对象,即隐式值,注入到函数参数函数体使用

2.1K20

如果我去参加前端面试,我应该能做出大圣老师这道题...

毕竟,我们网页就是一棵 DOM 树,从根部有节点,节点还有节点,对于每个节点,我们能够知道这个节点是什么标签并且对其节点做同样事就可以了 然后我们捋一下需要哪些技术细节: 首先我们应该获取根节点...,这个好说,我们浏览器控制台里试一试就知道:document.children[0] 然后我们应该能够获取每个标签对象字符串名字和节点列表,分别是 tagName 和 children 至于如何实现...这里未必要用到递归,我用是宽度优先搜索 BFS ,简单一个队列就能实现 值得一提是,我近一个月里写了基于 C++ 、Python 、 JavaScript/TypeScript 、 Scala/Java...,所以我也记不住 JavaScript API ,我都是浏览器控制台里试出来,比如 获取标签名字是 tagName 、 获取节点 Array 是 children 。...注意我标注 !!! 两行,这里有一个问题: dict = {} ,对于未声明过键值,如果直接调用运算,会报错 dict[未声明键值] +=1 // 报错!

48330

递归与尾递归简析

递归调用是函数最后执行一步时,该递归函数就是尾递归。 与之相对是非尾递归函数,你先执行递归调用,然后获取递归调用结果进行计算, 这样你需要先获取每次递归调用结果,才能获取最后计算结果。...看下面计算n阶乘函数,它是一个非尾递归函数。我们发现cal(n-1)返回值被cal(n)使用,因此对cal(n-1)调用并不是cal(n)所做最后一步。...,编译器优化尾部递归函数思想很简单,因为递归调用是最后一条statement,所以在当前函数没有什么可做,这样没有必要保存当前函数堆栈结构了。...而非尾递归函数调用过程当中系统为每一层返回点、局部量等开辟了栈来存储,因此递归次数过多容易造成栈溢出。 一个non-tail递归函数可以优化成尾递归函数吗?...我们还是以n阶乘为例,其方法是再使用一个参数,并在第二个参数累积阶乘值。当n达到0时,返回累积值。

80530
领券