使用 Shell 脚本深入了解函数复杂性 – 第七部分
我之前的文章“理解和编写 Shell 脚本中的函数”可能已经为您提供了如何在 Shell 脚本下编写函数的基本概念。现在是时候更深入地了解函数特性,例如局部变量和递归的使用。
局部变量
是什么使变量成为局部变量?这取决于声明变量的特定块。声明为 local 的变量可以从它出现的代码块访问,即它的作用域是本地的。为了解释这一点,让我们看一下下面的一个例子。
#!/bin/bash
func( ) {
local i=10
j=20
echo "i from func = $i"
echo "j from func = $j"
}
echo "i outside func = $i"
echo "j outside func = $j"
func
echo "i outside func = $i"
echo "j outside func = $j"
exit 0
执行上述脚本时,输出将是。
i outside func =
j outside func =
i from func = 10
j from func = 20
i outside func =
j outside func = 20
这是因为在执行前 2 条 echo 语句时,函数 func 尚未调用。调用函数 func 后,相同的 2 个 echo 语句 会产生不同的结果。现在,变量 j 是在 func 内部声明的,而不是本地的,以后就可以访问了。
因此,j 的值变为 20。局部变量i怎么样?由于其作用域位于函数 func 内部,因此无法从外部访问值 10。请注意,通常在 func 内声明的变量 j 默认情况下是全局的。
现在您已经熟悉了局部变量以及如何在功能块中使用它们。让我们继续讨论函数中最有趣的部分,即递归。
什么是递归?
函数调用自身通常称为递归过程。或者它可以定义为使用同一算法的更简单版本来表达算法。考虑查找数字的阶乘的示例。我们知道n!=1 x 2 x 3 x … x (n-1) x n。因此我们可以将递推关系写为:
n! = (n-1)! x n
因此,我们很容易递归调用同一个函数,并使用每次调用的返回值与之前的结果相乘,即
5! = 4! x 5
4! = 3! x 4
3! = 2! x 3
2! = 1! x 2
1! = 0! x 1
使用局部变量的递归
在这里,我们尝试编写一个脚本,使用局部变量和递归来查找数字的阶乘。
#!/bin/bash
fact( ) {
local num=$1
if [ $num -eq 0 ]; then
ret=1
else
temp=$((num-1))
fact $temp
ret=$((num*$?))
fi
return $ret
}
fact 5
echo "Factorial of 5 = $?"
exit 0
num 是一个局部变量,用于存储每次调用时的每个 n-1 值。这里的基本条件检查数字是否等于 0(因为 0!= 1 并且阶乘没有为负数定义)。到达此基本条件后,它会将值 1 返回给其调用者。现在num=1和ret=1 x 1。
此时,它向调用者返回1。现在num=2和ret=2 x 1等等。最后,当num=5时,返回值为24,最终结果为ret=5 x 24。最终结果120被传递到初始调用者语句并显示。
上面的脚本有一个问题。正如我在上一篇文章中所解释的,函数不能返回大整数。因此,上述问题的解决方案就留给用户了。
问:我们可以在不使用局部变量的情况下执行递归吗?答案是是。
没有局部变量的递归
请看以下使用递归显示斐波那契数列的示例。基本递推关系为:
fib(0) = 0
fib(1) = 1
else
fib(n) = fib(n-1) + fib(n-2)
Fibonacci series using recursion
#!/bin/bash
fib( ) {
a=$1
if [ $a -lt 2 ]; then
echo $a
else
((--a))
b=$(fib $a)
((--a))
c=$(fib $a)
echo $((b+c))
fi
}
for i in $(seq 0 15)
do
out=$(fib $i)
echo $out
done
exit 0
上述脚本中没有使用局部变量。我希望你能理解脚本执行过程中的流程。
这里的值15代表要显示的斐波那契数列的项数。您是否注意到上述脚本的执行有什么特别之处?这需要一段时间,不是吗?脚本中的递归比 C 等编程语言中的递归慢。
我打算通过这篇文章来总结 shell 脚本中的函数部分。请随时关注Howtoing,以获取即将发布的有关数组的文章以及更多内容……