轨道
/
jq
jq
/
练习
/
递归函数
递归函数

递归函数

学习练习

简介

递归

递归函数是调用自身的函数。

一个_递归函数_至少需要一个_基准情况_和一个_递归情况_。

基准情况在不再次调用函数的情况下返回一个值。 递归情况会再次调用函数,并修改输入,使其最终能够匹配基准情况。

下面是一个统计数组中元素个数的例子。

def count:
  if length == 0 then
    0                       # base case
  else
    1 + (.[1:] | count)     # recursive case
  end;

([] | count),           # => 0
([11, 22, 33] | count)  # => 3

一个_递归函数_可以有多个_基准情况_,也可以有多个_递归情况_。 例如,斐波那契数列就是一个有两个_基准情况_的递归数列。

def fibonacci:
  if . == 0 then
    0
  elif . == 1 then
    1
  else
    (. - 1 | fibonacci) + (. - 2 | fibonacci)
  end;

10 | fibonacci          # => 55

统计给定值x在数组中出现的次数,会用到两个_递归情况_。

def count_occurrences(x):
  if length == 0 then
    0
  elif first == x then
    1 + (.[1:] | count_occurrences(x))
  else
    (.[1:] | count_occurrences(x))
  end;

[11, 22, 33, 22, 44] | count_occurrences(22)    # => 2

在实践中,迭代数组以及其他可枚举的数据结构,通常使用内置函数完成,比如map和reduce,或者使用流,例如[.[] | select(...)]。 在底层,有些内置函数就是用递归实现的。

说明

你刚加入了一个维护基于 jq 的数据管道的团队。 第一次结对时,你的新同事好奇起来: “你知道add、reverse和map底层是怎么工作的吗?我们来探究一下:试着用递归自己实现它们,不要用内置的。”

1. 实现一个函数,把数组中的数字相加

我们将重新实现内置的add过滤器,来练习递归。 _基线条件_是:空数组的和为0。

请用递归函数自己实现,不要使用内置的add。

[5, 4, 6, 10] | array_add     # => 25

2. 反转数组

我们将重新实现内置的reverse过滤器。 _基线条件_是:空数组反转后仍是空数组。

请用递归函数自己实现,不要使用内置的reverse。

[5, 4, 6, 10] | array_reverse   # => [10, 6, 4, 5]

3. 映射数组

我们将重新实现内置的map过滤器。 这个函数接收一个过滤器作为参数,对输入数组的每个元素运行该过滤器,并把输出放进一个新数组返回。 _基线条件_是:空数组映射后仍是空数组。

请用递归函数自己实现,不要使用内置的map。

[5, 4, 6, 10] | array_map(. + 10)   # => [15, 14, 16, 20]
通过 GitHub 编辑 链接将在新窗口或新标签页中打开
jq Exercism

准备好开始 递归函数 了吗?

注册 Exercism,借助 12 个概念75 个练习 和真人导师指导,学习并掌握 jq,全部免费。