将切片传递给Python中的递归函数

时间:2018-01-16 19:43:26

标签: python list recursion slice tail-recursion

我认为我理解递归函数和切片(单独)的基本原理,但我无法将它们组合在一起。我不明白将切片或列表传递给以下函数时会发生什么。

def listSum(ls):
    # Base condition
    if not ls:
        return 0

    # First element + result of calling `listsum` with rest of the elements
    return ls[0] + listSum(ls[1:])

listSum([1, 3, 4, 5, 6])
19

这是包含此代码的线程: Understanding recursion in Python

我想我会真正受益于调用函数时会发生的事情。

该线程有很多不同的递归函数的例子,具有预期的输出。我理解后面的例子甚至比上面复制的例子还少。

2 个答案:

答案 0 :(得分:0)

递归的基本概念是每次调用消耗输入的一部分,减少它直到它匹配基本情况

此处,基本情况为if not ls,当ls为空列表时为真。

消耗是通过切片完成的:每个调用都传递一个比最后一个短一个元素的列表。这是通过listSum(ls1:])完成的,listSum在由ls 的所有元素组成的列表上调用ls,除了第一个

然后将第一个元素添加到递归调用的结果中并返回。由于ls元素会有一次递归调用,{{1}}中的每个数字轮流都会被弹出并求和,即消耗

一步一步地完成它,我们将添加

  

1 + listSum([3,4,5,6])

  

1 + 3 + listSum([4,5,6])

  

1 + 3 + 4 + listSum([5,6])

  

1 + 3 + 4 + 5 + listSum([6])

  

1 + 3 + 4 + 5 + 6 + listSum([])

,由于基本情况,

  

1 + 3 + 4 + 5 + 6 + 0

这是19。

答案 1 :(得分:0)

列出遍历宝贝步骤

对于使用递归遍历列表,只有3件事情重要

  • 空列表 - 始终检查您的列表是否为空!
  • 列表的头部 - 第一项
  • 列表的尾部 - 整个列表除了第一个项目

好的,这是我们用来编写程序的函数

def is_empty (ls):
  return ls == []

def head (ls):
  return ls[0]

def tail (ls):
  return ls[1:]

功能的使用很简单

is_empty ([])           # => True
is_empty ([ 1, 2, 3 ])  # => False

head ([ 1, 2, 3 ])      # => 1

tail ([ 1, 2, 3 ])      # => [2,3]

好的,让我们编写我们的list_sum函数 - 我想你会同意我们最终得到一个非常易读的程序,这要归功于我们定义的列表助手

def list_sum (ls):
  if is_empty (ls):
    return 0
  else:
    return head (ls) + list_sum (tail (ls))

print (list_sum ([ 1, 2, 3, 4, 5, 6, 7 ]))
# => 28

但是您使用尾递归标记了这一点,因此在可选参数和默认值的帮助下,我们可以将递归调用移动到尾部位置。 粗体

的变化
def list_sum (ls, acc = 0):
  if is_empty (ls):
    return acc
  else:
    return list_sum (tail (ls), head (ls) + acc)

print (list_sum ([ 1, 2, 3, 4, 5, 6, 7 ]))
# => 28

但是Python并没有真正消除尾部调用,所以你仍然需要something else来确保堆栈安全。

每天吸烟高阶函数

使用headtail使我们摆脱了[0][1:]的乏味(和丑陋),但这种列表遍历的模式非常普遍,我们可以解除我们甚至完全在这个层面上思考!

让我们回顾一下我们的函数list_sum。此函数遍历列表并使用内置+函数执行添加。如果我们想要使用+而不是*来代替所有元素,那该怎么办?

def list_sum (ls):
  if is_empty (ls):
    return 0
  else:
    return head (ls) + list_sum (tail (ls))

def list_product (ls):
  if is_empty (ls):
    return 1
  else:              
    return head (ls) * list_product (tail (ls))

print (list_product ([ 1, 2, 3, 4, 5, 6, 7 ]))
# => 5040

唯一不同的是0更改为1+更改为*。每次我们想要对列表执行某些操作时,我们都不想重写所有这些内容。如果我们使用函数参数抽象这些值,我们可以在这个漂亮的小程序中捕获列表遍历的本质

from operator import add, mul

def list_reduce (f, acc, ls):
  if is_empty (ls):
    return acc
  else:
    return list_reduce (f, f (acc, head (ls)), tail (ls))

def list_sum (ls):
  return list_reduce (add, 0, ls)

def list_product (ls):
  return list_reduce (mul, 1, ls)

print (list_sum ([ 1, 2, 3, 4, 5, 6, 7 ]))
# => 28

print (list_product ([ 1, 2, 3, 4, 5, 6, 7 ]))
# => 5040

可以使用其他高阶函数构建高阶函数。然后在你知道它之前,你正在进行各种高级转换和遍历!

请注意我们此时并未考虑headtail。像not []ls[0]ls[1:]这样难以处理的事情是不可见的,因此是不可思议的。

def map (f, ls):
  return list_reduce (lambda acc, x: acc + [ f (x) ], [], ls)

def square (x):
  return x * x

print (map (square, [ 1, 2, 3, 4, 5, 6, 7 ]))
# => [1, 4, 9, 16, 25, 36, 49]