Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Lập Trình Bash

Thành thạo chức năng Shell: Kỹ thuật nâng cao trong Shell Scripting – Phần VII

Bài trước về "Hiểu và viết hàm trong Shell Scripts" có thể đã cho bạn ý tưởng cơ bản về cách viết hàm trong shell script. Bây giờ là lúc đi sâu vào các tính năng hàm như biến cục bộ và đệ quy.

Biến cục bộ (Local Variables)

Điều gì làm một biến trở thành cục bộ? Phụ thuộc vào khối mã cụ thể nơi biến được khai báo. Biến được khai báo là local chỉ truy cập được từ khối mã đó, tức là phạm vi cục bộ. Để hiểu rõ, xem ví dụ dưới đây.

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

Khi chạy script trên, kết quả sẽ là:

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

Điều này xảy ra vì hàm func chưa được gọi khi 2 lệnh echo đầu tiên thực thi. Sau khi gọi hàm func, 2 lệnh echo cho kết quả khác. Biến j khai báo trong func nhưng không phải local, có thể truy cập sau đó. Do đó giá trị j trở thành 20. Còn biến cục bộ i? Vì phạm vi nằm trong hàm func, giá trị 10 không truy cập được từ bên ngoài. Lưu ý biến j khai báo bình thường trong func là toàn cục mặc định.

Bây giờ bạn đã quen với biến cục bộ và cách dùng trong khối hàm. Hãy chuyển sang phần thú vị nhất dưới hàm: đệ quy.

Đệ quy (Recursion) là gì?

Hàm tự gọi chính nó thường được gọi là thủ tục đệ quy. Hoặc định nghĩa là biểu diễn thuật toán bằng phiên bản đơn giản hơn của thuật toán đó. Ví dụ tìm giai thừa của số. Ta biết n! = 1 x 2 x 3 x ... x (n-1) x n. Vậy có thể viết quan hệ đệ quy:

n! = (n-1)! x n

Vậy dễ dàng gọi đệ quy cùng hàm và dùng giá trị trả về từ mỗi lần gọi nhân với kết quả trước:

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

Đệ quy sử dụng biến cục bộ

Ở đây thử viết script tìm giai thừa dùng biến cục bộ và đệ quy.

#!/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 "Giai thừa của 5 = $?"
exit 0

num là biến cục bộ lưu giá trị n-1 mỗi lần gọi. Điều kiện cơ sở kiểm tra số có bằng 0 không (vì 0! = 1 và giai thừa không xác định cho số âm). Khi chạm điều kiện cơ sở, trả về 1 cho caller. Lúc này num = 1 và ret = 1 x 1. Trả về 1 cho caller tiếp theo. num = 2 và ret = 2 x 1 và cứ thế. Cuối cùng num = 5 giá trị trả về là 24 và kết quả cuối ret = 5 x 24. Kết quả 120 truyền về lời gọi ban đầu và hiển thị.

Có một vấn đề trong script trên. Như giải thích bài trước, hàm không thể trả về số nguyên lớn. Vì vậy để lại cho người dùng tìm giải pháp.

Câu hỏi: Có thể đệ quy không dùng biến cục bộ? Câu trả lời là Có.

Đệ quy không dùng biến cục bộ

Xem ví dụ hiển thị dãy Fibonacci dùng đệ quy. Quan hệ đệ quy cơ bản:

fib(0) = 0
fib(1) = 1
else
fib(n) = fib(n-1) + fib(n-2)

Dãy Fibonacci dùng đệ quy
#!/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

Không dùng biến cục bộ trong script trên. Hy vọng bạn hiểu luồng thực thi.

Giá trị 15 đại diện số hạng trong dãy Fibonacci cần hiển thị. Bạn có nhận ra gì đặc biệt về việc thực thi script không? Nó mất một chút thời gian, đúng không? Đệ quy trong script chậm hơn đệ quy trong ngôn ngữ như C.

Với bài này, tôi kết thúc phần hàm trong shell scripting. Cập nhật với Tecmint để có bài tiếp theo về mảng và hơn thế nữa...