Wyszukiwanie w witrynie

Głębiej o złożonościach funkcji w skryptach powłoki – część VII


Mój poprzedni artykuł na temat „Zrozumienie i pisanie funkcji w skryptach powłoki” mógł dać ci podstawowe pojęcie o tym, jak pisać funkcje w skryptach powłoki. Nadszedł czas, aby zagłębić się w funkcje funkcjonalne, takie jak użycie zmiennych lokalnych i rekurencja.

Zmienne lokalne

Co sprawia, że zmienna jest lokalna? Zależy to od konkretnego bloku, w którym zadeklarowana jest zmienna. Zmienna zadeklarowana jako lokalna będzie dostępna z tego bloku kodu, w którym się pojawia, tj. jej zakres jest lokalny. Aby wyjaśnić tę kwestię, spójrzmy na jeden przykład poniżej.

#!/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

Po wykonaniu powyższego skryptu wynik będzie następujący.

i outside func = 
j outside func = 
i from func = 10 
j from func = 20 
i outside func = 
j outside func = 20

Dzieje się tak, ponieważ funkcja func nie została jeszcze wywołana podczas wykonywania pierwszych 2 instrukcji echo. Po wywołaniu funkcji func te same 2 instrukcje echo dają inny wynik. Teraz zmienna j, która została zadeklarowana wewnątrz func, a nie lokalnie, była dostępna później.

Zatem wartość j wynosi 20. A co ze zmienną lokalną i? Ponieważ jej zakres znajdował się wewnątrz funkcji func, nie można było uzyskać dostępu do wartości 10 z zewnątrz. Zauważ, że zmienna j zwykle deklarowana wewnątrz func jest domyślnie globalna.

Teraz znasz zmienne lokalne i wiesz, jak ich używać w blokach funkcyjnych. Przejdźmy do najciekawszej części funkcji, czyli rekurencji.

Co to jest rekurencja?

Funkcja wywołująca samą siebie jest ogólnie nazywana procedurą rekurencji. Można to też zdefiniować jako wyrażenie algorytmu przy użyciu prostszej wersji tego samego algorytmu. Rozważmy przykład znajdowania silni liczby. Wiemy, że n!=1 x 2 x 3 x … x (n-1) x n. Relację powtarzania możemy zatem zapisać jako:

n! = (n-1)! x n

Dlatego łatwo jest nam rekursywnie wywoływać tę samą funkcję i używać wartości zwracanej z każdego wywołania do pomnożenia przez poprzedni wynik, tj.

5! = 4! x 5
4! = 3! x 4
3! = 2! x 3
2! = 1! x 2
1! = 0! x 1

Rekurencja z wykorzystaniem zmiennych lokalnych

Tutaj próbujemy napisać skrypt do znajdowania silni liczby za pomocą zmiennych lokalnych i rekurencji.

#!/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 to zmienna lokalna używana do przechowywania każdej wartości n-1 w każdym wywołaniu. Tutaj warunek podstawowy sprawdza, czy liczba jest równa zero, czy nie (ponieważ 0!=1 i silnia nie jest zdefiniowana dla liczb ujemnych). Po osiągnięciu tego warunku podstawowego zwraca wartość 1 swojemu wywołującemu. Teraz num=1 i ret=1 x 1.

W tym momencie zwraca 1 swojemu wywołującemu. Teraz num=2 i ret=2 x 1 i tak dalej. Wreszcie, gdy num=5 zwrócona wartość wyniesie 24, a wynik końcowy to ret=5 x 24. Końcowy wynik 120 jest przekazywany do początkowej instrukcji dzwoniącego i wyświetlany.

W powyższym skrypcie jest jeden problem. Jak wyjaśniłem w poprzednim artykule, funkcje nie mogą zwracać dużych liczb całkowitych. Znalezienie rozwiązania powyższego problemu pozostawiono więc użytkownikom.

Pyt. Czy możemy wykonać rekurencję bez użycia zmiennych lokalnych? Odpowiedź brzmiTak.

Rekurencja bez zmiennych lokalnych

Spójrz na poniższy przykład wyświetlania szeregu Fibonacciego przy użyciu rekurencji. Podstawowa relacja powtarzalności to:

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

W powyższym skrypcie nie są używane żadne zmienne lokalne. Mam nadzieję, że rozumiesz przebieg skryptu podczas jego wykonywania.

Tutaj wartość 15 reprezentuje liczbę terminów z szeregu Fibonacciego, które mają zostać wyświetlone. Czy zauważyłeś coś szczególnego w związku z wykonaniem powyższego skryptu? To zajmuje trochę czasu, prawda? Rekurencja w skrypcie jest wolniejsza niż rekurencja w językach programowania, takich jak C.

W tym artykule planuję zakończyć część dotyczącą funkcji w skryptach powłoki. Bądź na bieżąco z Tecmint, aby otrzymywać nadchodzące artykuły na temat tablic i wiele więcej…