فصل چهاردهم - توابع

از Linuxreview Wiki
پرش به: ناوبری, جستجو

در این فصل ما به بحث در مورد یکی از مفاهیم مهم و اساسی در برنامه نویسی به نام تابع می پردازیم. توابع ابزاری هستند که به ما در بهبود برنامه کمک می کنند و برنامه نویسی ساخت یافته را ارائه می دهند، بدین معنا که برنامه اصلی به قسمتهای منطقی و مستقل کوچکتری تقسیم می شود که توابع نام دارند.

محتویات

[نهفتن]

ایجاد و استفاده از توابع

شکل کلی اعلان یک تابع در شل به صورت زیر است:

function_name() {
	commandlist
} 

function function_name {
	commandlist
} 

در اینجا function_name به مجموعه دستورات commandlist مقید میشود. به قسمت commandlist بدنه‌ی تابع نیز گفته میشود.

نکته: در صورتی که در فرم دوم از کلمه function در ابتدای اعلان تابع استفاده نشود، مانند شکل اول استفاده از () ضروری است.
نکته: توجه کنید که قسمت بدنه (commandlist) نمیتواند خالی باشد. و اعلان تابع با بدنه‌ی خالی منجر به بروز خطا میشود.



مثال:

lsl() { ls -l ; } # valid
function lsl{ ls -l ; } # valid
lsl { ls -l ; }   # invalid

در این مثال دستور اول و دوم یک اعلان صحیح برای تابع و دستور سوم اعلانی اشتباه است.

نکته: یکی از قابلیت‌های مهم توابع در شل امکان جایگزین آنها به‌جای دستورات خود شل با نام همان دستورات است.
مثال زیر را در نظر بگیرید:

cd () {
chdir ${1:-$HOME}
PS1="`pwd`$ "
export PS1
}

در اینجا عملکرد این تابع جایگزین دستور cd میشود.


فراخوانی توابع

برای فراخواندن توابع در شل تنها نوشتن نام آنها کافی است.

نکته: در هنگام فراخوانی یک تابع از () در انتهای نام استفاده نمیشود و استفاده از این فرم برای فراخوانی یک تابع منجر به بروز اشتباه میشود.



مثال: چنانچه فراخوانی تابع ls1 به صورت ()ls1 انجام شود، شل آن را به عنوان یک اعلان جدید در نظر میگیرد و منتظر خط فرمان برای دریافت بدنه‌ی تابع به صورت < درمی‌آید.


مثال: با استفاده از اسکریپت زیر میتوان لیست دایرکتوری‌ها در مسیر جاری را بدست آورد به طوری که هر دایرکتوری در یک خط نمایش داده میشود.

OLDIFS="$IFS"
IFS=:
for DIR in $PATH ; do
    echo $DIR 
done
IFS="$OLDIFS"

برای اینکه بتوانید از این اسکریپت بارها و بارها به سادگی استفاده کنید، میتوانید آن‌را در قالب یک تابع تعریف کنید.

lspath() {
    OLDIFS="$IFS"
    IFS=:
    for DIR in $PATH ; do 
        echo $DIR
    done
    IFS="$OLDIFS"
}

حال میتوانید با استفاده از عبارت lspath این تابع را اجرا کنید.
مثال:

$ lspath
/sbin
/bin
/usr/bin
/usr/sbin
/opt/bin
/usr/ucb
/usr/ccs/bin
/usr/openwin/bin

همچنین به سادگی میتوانید این دستور را با هر کدام از دستورات دیگر شل ترکیب کنید.

$ lspath | grep bin

حوزه‌ی متغیر

برای برنامه نویس‌‌هایی که از زبان‌های برنامه دیگر استفاده میکنند شاید قواعد حوزه‌ها در شل کمی متفاوت باشد. در واقع در شل هیچ حوزه‌ای برای استفاده از متغیری که تعریف شده است وجود ندارد و میتوان از آن در هر جایی استفاده کرد. یا از متغیری که در بدنه یک تابع تعریف شده در تابع دیگر استفاده نمود و به دلخواه تغییراتی را در آن اعمال کرد.
مثال:

myfunc() {
    echo "I was called as : $@"
    x=2
}

### Main script starts here 

echo "Script was called with $@"
x=1
echo "x is $x"
myfunc 1 2 3
echo "x is $x"

که منجر به تولید خروجی زیر میشود:

Script was called with a b c
x is 1
I was called as : 1 2 3
x is 2

همانطور که مشاهده میکنید تغییراتی که در سطح تابع myFunc بر روی متغیر x انجام میشود، بر روی کل متغیرهای x اعمال میشود و حتی بعد از اتمام تابع myFunc نیز همچنان باقی می‌ماند.
مثال:

myfunc() {
    echo "\$1 is $1"
    echo "\$2 is $2"
    a="Goodbye Cruel"
}

a=Hello
b=World
myfunc $a $b
echo "a is $a"
echo "b is $b"

که خروجی زیر را تولید میکند:

$1 is Hello
$2 is World
a is Goodbye Cruel
b is World

توابع بازگشتی

در شل این امکان وجود دارد تا یک تابع خودش را فراخوانی کند. در این حالت به این تابع اصطلاحا "تابع بازگشتی" گفته میشود. برای جلوگیری از تکرار بی‌نهایت فراخوانی‌ها یک شرط پایان برای بازگشت تابع به جایی فراخوانده شده وجود دارد.
مثال: پیاده سازی تابع محاسبه‌ی فاکتوریل یک عدد به صورت بازگشتی

factorial(){
    if [ "$1" -gt "1" ]; then
        i=$[$1-1]
        j=`factorial $i`
        k=$[$1*j]
        echo $k
    else
        echo 1
    fi
}

echo "Enter a number:"
read x
factorial $x

تعریف توابع تودرتو

در شل این امکان وجود دارد تا بتوان یک تابع را در بدنه‌ی تابع دیگر تعریف کرد. شکل کلی اعلان چند تابع به صورت تودرتو به صورت زیر است:

f1(){    
    ...
    f2(){ # nested
        commandlist
    }
    ...
}

توجه کنید که در این حالت تلاش برای دسترسی مستقیم به تابع داخلی (f2) منجر به بروز خطا خواهد شد.
مثال:

f1()
{    
    x=2
    f2(){
        x=$[x+2]
        echo $x
    }
    f2
}

در این مثال در صورتی که بخواهید تابع f2 را در جایی خارج از بدنه تابع f1 فراخوانی کنید با پیام خطای زیر مواجه خواهید شد:

f2: command not found

نکته: در صورتی در یک اسکریپت تابعی دوبار یا چند بار اعلان مجدد شود، آخرین اعلان مد نظر قرار میگیرد.


مثال:

func (){
    echo "First version of func ()."
}

func (){
    echo "Second version of func ()."
}
 
func

که منجر به تولید خروجی زیر میشود:

Second version of func ().



Eman ‏۱۹ فوریهٔ ۲۰۱۲، ساعت ۰۷:۰۸ (UTC)

ابزارهای شخصی
گویش‌ها
فضاهای نام
عملکردها
گشتن
کتاب‌ها
مقاله‌ها
جعبه‌ابزار