Scala:列表上的递归求和

Scala:列表上的递归求和,scala,recursion,Scala,Recursion,谁能解释一下最后一行吗 那么中间结果存储在哪里呢 x.头+和(x.尾) 在+之后,它是否提供了一个要添加的元素?解释递归IMHO的最好方法是一步一步地进行,看看实际发生了什么。另一件有帮助的事情是添加return语句,特别是如果您来自Java这样的语言,因为它更容易理解正在发生的事情 返回的函数如下所示: def sum(xs: List[Int]): Int = { if(xs.isEmpty) 0 else xs.head + sum(xs.tail) } def

谁能解释一下最后一行吗

那么中间结果存储在哪里呢 x.头+和(x.尾)
在+之后,它是否提供了一个要添加的元素?

解释递归IMHO的最好方法是一步一步地进行,看看实际发生了什么。另一件有帮助的事情是添加return语句,特别是如果您来自Java这样的语言,因为它更容易理解正在发生的事情

返回的函数如下所示:

def sum(xs: List[Int]): Int = {
  if(xs.isEmpty)
    0
  else
    xs.head + sum(xs.tail)
}
def sum(xs: List[Int]): Int = {
  if(xs.isEmpty)
    return 0
  else
    return xs.head + sum(xs.tail)
}
在您的例子中,有一个函数可以对列表中的所有整数求和

让我们想象一下,您使用具有以下值(1,2,3)的列表调用了函数

函数将如何运行

第一个函数调用如下所示:

def sum(xs: List[Int]): Int = {
  if(xs.isEmpty)
    0
  else
    xs.head + sum(xs.tail)
}
def sum(xs: List[Int]): Int = {
  if(xs.isEmpty)
    return 0
  else
    return xs.head + sum(xs.tail)
}
第二次呼叫现在是列表(2,3):

Trird呼叫现在与列表(3)一起:

第四个呼叫为空列表:

if(xs.isEmpty) // false - it has 1 elements (3)
   return 0 // skipped
else
   return 3 + sum(()) // xs.head is changed with 3 and xs.tail is empty list now
现在我们的求和调用堆栈如下所示:

 if(xs.isEmpty) // true
    return 0 // Recursion termination case triggered
我们只是开始返回值:

sum((1,2,3))   
   where sum = 1 + sum((2,3))   
       where sum = 2 + sum((3))   
          where sum = 3 + sum(())    
             where sum = 0
所以我们的结果是和((1,2,3))=6


这就是为什么我们不需要存储“中间结果”,因为计算总和从末尾开始并向后滚动。

解释递归IMHO的最好方法是一步一步地进行,看看实际发生了什么。另一件有帮助的事情是添加return语句,特别是如果您来自Java这样的语言,因为它更容易理解正在发生的事情

返回的函数如下所示:

def sum(xs: List[Int]): Int = {
  if(xs.isEmpty)
    0
  else
    xs.head + sum(xs.tail)
}
def sum(xs: List[Int]): Int = {
  if(xs.isEmpty)
    return 0
  else
    return xs.head + sum(xs.tail)
}
在您的例子中,有一个函数可以对列表中的所有整数求和

让我们想象一下,您使用具有以下值(1,2,3)的列表调用了函数

函数将如何运行

第一个函数调用如下所示:

def sum(xs: List[Int]): Int = {
  if(xs.isEmpty)
    0
  else
    xs.head + sum(xs.tail)
}
def sum(xs: List[Int]): Int = {
  if(xs.isEmpty)
    return 0
  else
    return xs.head + sum(xs.tail)
}
第二次呼叫现在是列表(2,3):

Trird呼叫现在与列表(3)一起:

第四个呼叫为空列表:

if(xs.isEmpty) // false - it has 1 elements (3)
   return 0 // skipped
else
   return 3 + sum(()) // xs.head is changed with 3 and xs.tail is empty list now
现在我们的求和调用堆栈如下所示:

 if(xs.isEmpty) // true
    return 0 // Recursion termination case triggered
我们只是开始返回值:

sum((1,2,3))   
   where sum = 1 + sum((2,3))   
       where sum = 2 + sum((3))   
          where sum = 3 + sum(())    
             where sum = 0
所以我们的结果是和((1,2,3))=6


这就是我们不需要存储“中间结果”的原因,因为计算总和从末尾开始并向后滚动。

从实际示例的角度考虑可能会对您有所帮助。假设我们有:

sum((1,2,3))   
       where sum = 1 + 5   
           where sum = 2 + 3  
              where sum = 3 + 0 
将其传递给
sum
方法,第一个测试将失败(列表不是空的)。然后,它会将标题项(即1)添加到尾部的
总和,即
总和(列表(3,5))
。因此,在计算第二个表达式并且第二次调用
sum
之前,操作无法完成。初始测试失败(
List(3,5)
不为空),该方法返回值
3+sum(List(5))
。同样,在计算第二个表达式之前,它无法完成,因此再次调用
sum
。初始测试再次失败,因为
List(5)
不是空的,并且此调用返回的值为
5+sum(List())
。最后一次调用了
sum,这次初始测试成功并返回0,因此:

List(1,3,5)

弄清楚这类事情对于理解递归是有用的(而且是必要的)。

从实际例子的角度思考可能会有帮助。假设我们有:

sum((1,2,3))   
       where sum = 1 + 5   
           where sum = 2 + 3  
              where sum = 3 + 0 
将其传递给
sum
方法,第一个测试将失败(列表不是空的)。然后,它会将标题项(即1)添加到尾部的
总和,即
总和(列表(3,5))
。因此,在计算第二个表达式并且第二次调用
sum
之前,操作无法完成。初始测试失败(
List(3,5)
不为空),该方法返回值
3+sum(List(5))
。同样,在计算第二个表达式之前,它无法完成,因此再次调用
sum
。初始测试再次失败,因为
List(5)
不是空的,并且此调用返回的值为
5+sum(List())
。最后一次调用了
sum,这次初始测试成功并返回0,因此:

List(1,3,5)
弄清楚这类事情对于理解递归非常有用(而且是必要的)。

中间结果(xs.head和sum(xs.tail))都存储在所谓的帧中,这些帧是执行线程Java堆栈中的内存区域。 为sum函数的每个嵌套调用创建一个单独的框架,以便这些中间结果对于每个sum调用都是单独的

发件人:

框架用于存储数据和部分结果,以及执行动态链接、方法返回值和分派异常

每次调用方法时都会创建一个新的框架。帧在其方法调用完成时被销毁,无论该完成是正常的还是突然的(它抛出一个未捕获的异常)。帧是从创建帧的线程的Java虚拟机堆栈(§2.5.2)分配的。每个帧都有自己的局部变量数组(§2.6.1)、自己的操作数堆栈(§2.6.2)和对当前方法类的运行时常量池(§2.5.5)的引用

以下是您的代码如何编译为JVM字节码:

sum(List()) = 0
sum(List(5)) = 5
sum(List(3,5)) = 8
sum(List(1,3,5)) = 9
public int sum(scala.collection.immutable.List);
代码:
0:aload_1
1:invokevirtual#63//方法scala/collection/immutable/List.isEmpty:()Z
4:ifeq 11
7:iconst_0
8:goto 30
11:aload_1
12:invokevirtual#67//方法scala/collection/immutable/List.head:()Ljava/lang/Object;
15:invokestatic#73//方法scala/runtime/BoxesRunTime.unextpoint:(Ljava/lang/Object;)I
18:aload_0
19:aload_1
20:invokevirtual#76//方法scala/collection/immutable/List.tail:()Ljava/lang/Object;
23:checkcast#59//类scala/collection/immutable/List
26:invokevir