网站搜索

使用 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=1ret=1 x 1

此时,它向调用者返回1。现在num=2ret=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,以获取即将发布的有关数组的文章以及更多内容……