یک عدد صحیح را به عوامل ضرب اول تجزیه میکند.
bash$ factor 27417 27417: 3 13 19 37
#!/bin/bash # #روش سریع و آسان تولید اعداد اول، بدون توسل #+به الگوریتمهای خاص. CEILING=10000 # PRIME=0 E_NOTPRIME= is_prime () { local factors factors=( $(factor $1) ) #تخصیص خروجی factor به آرایه. if [ -z "${factors[2]}" ] #${factors[2]} سومین عضو آرایه factors است و #+دومین عامل ضرب شناسه است. اگر این عضو آرایه #خالی باشد، یعنی دومین فاکتور وجود ندارد، و #+در اینصورت شناسه، عدد اول است. then return $PRIME # else return $E_NOTPRIME # fi } echo for n in $(seq $CEILING) do if is_prime $n then printf %5d $n fi # done #برای $CEILING بالاتر، به عدد بزرگتر مناسبی تنظیم کنید. echo exit
Bash نمیتواند محاسبات ممیز شناور را به کار ببرد، و فاقد عملگرهایی برای توابع مهم ریاضیات است. خوشبختانه، bc برای نجاتبخشی شتاب میکند.
bc نه فقط یک برنامه سودمند فراگیرنده محاسبات با دقت اختیاری است، بلکه بسیاری از امکانات یک زبان برنامهنویسی را ارایه میکند. این برنامه دارای ترکیب دستوری تا حدودی شبیه C است.
چون به طور مشخص یک برنامه خوش رفتار یونیکس است، و بنابراین میتواند در یک لوله به کار برود، bc در اسکریپتها سودمند واقع میشود.
این هم یک الگوی ساده برای استفاده از bc جهت محاسبه یک متغیر اسکریپت. این مثال از جایگزینی فرمان استفاده میکند.
variable=$(echo "OPTIONS; OPERATIONS" | bc)
مثال 16-47. قسط ماهانه یک وام
#!/bin/bash # #این یک بهبود کد بسته mcalc (mortgage calculator) نوشته Jeff Schmidt #+و Mendel Cooper (ارادتمند شما، نگارنده این راهنمای ABS) است. # echo echo "Given the principal, interest rate, and term of a mortgage," echo "calculate the monthly payment." bottom=1.0 echo echo -n "Enter principal (no commas) " read principal echo -n "Enter interest rate (percent) " #اگر 12% است، 12 وارد کنید نه .12 read interest_r echo -n "Enter term (months) " read term interest_r=$(echo "scale=9; $interest_r/100.0" | bc) # # #scale تعداد ارقام اعشار را تعیین میکند. interest_rate=$(echo "scale=9; $interest_r/12 + 1.0" | bc) top=$(echo "scale=9; $principal*$interest_rate^$term" | bc) # #فرمول استاندارد برای محاسبه سود. echo; echo "Please be patient. This may take a while." let "months = $term - 1" # for ((x=$months; x > 0; x--)) do bot=$(echo "scale=9; $interest_rate^$x" | bc) bottom=$(echo "scale=9; $bottom+$bot" | bc) # done # # #Rick Boivie یک پیادهسازی کارآمدترِ حلقه فوق را #+توضیح داده که مدت محاسبه را به دوسوم کاهش میدهد. # # # # #و سپس یک جایگزین بازهم کارآمدتر مطرح نمود، روشی #+که زمان اجرا را نزدیک به 95% تقلیل میدهد! # # # # # # # # #یک حلقه for داخل جایگزینی فرمان تعبیه میکند. # #از طرف دیگر، Frank Wang مورد زیر را پیشنهاد میکند: # #به علت اینکه . . . #الگوریتم پشت حلقه در حقیقت جمع جملههای یک تصاعد هندسی است. #فرمول مجموع جملهها e0(1-q^n)/(1-q) است که در آن e0 جمله اول #+است و q=e(n+1)/e(n) و n تعداد جملهها است. # # payment=$(echo "scale=2; $top/$bottom" | bc) #استفاده از دو رقم اعشار برای دلارها و سنتها. echo echo "monthly payment = \$$payment" #نمایش یک علامت دلار جلوی مبلغ. echo exit 0 #تمرینها: # #(1 برای اینکه کاراکترهای کاما در مقدار اصلی مجاز شود ورودی رافیلتر کنید. #(2 ورودی رافیلتر کنید تا وارد نمودن نرخ به صورت درصد یا اعشاری میسر گردد. #(3 اگر واقعاً مشتاق هستید، اسکریپت را جهت چاپ جدول کامل استهلاک بسط دهید.
#!/bin/bash # # # # # # # # # # # # # #==> با اجازه نویسنده اسکریپت در راهنمای ABS استفاده گردیده است. #==> توضیحاتی که به وسیله نگارنده راهنمای ABS اضافه گردیده است. NOARGS=85 PN=`basename "$0"` #نام برنامه VER=`echo '$Revision: 1.2 $' | cut -d' ' -f2` #==> نگارش 1.2 Usage () { echo "$PN - print number to different bases, $VER (stv '95) usage: $PN [number ...] If no number is given, the numbers are read from standard input. A number may be binary (base 2) starting with 0b (i.e. 0b1100) octal (base 8) starting with 0 (i.e. 014) hexadecimal (base 16) starting with 0x (i.e. 0xc) decimal otherwise (i.e. 12)" >&2 exit $NOARGS } #==> پیغام نحوه استفاده را چاپ میکند. Msg () { for i #==> in [list] غایب است. چرا؟ do echo "$PN: $i" >&2 done } Fatal () { Msg "$@"; exit 66; } PrintBases () { # Determine base of the number for i #==> in [list] غایب است... do #==> بنابراین روی شناسههای خطفرمان عمل میکند. case "$i" in 0b*) ibase=2;; # 0x*|[a-f]*|[A-F]*) ibase=16;; # 0*) ibase=8;; # [1-9]*) ibase=10;; # *) Msg "illegal number $i - ignored" continue;; esac #حذف پیشوند، تبدیل ارقام هگز به حرف بزرگ (bc این را لازم دارد). number=`echo "$i" | sed -e 's:^0[bBxX]::' | tr '[a-f]' '[A-F]'` #==> به جای / از : به عنوان جداکننده sed استفاده میکند. #تبدیل عدد به مبنای ده dec=`echo "ibase=$ibase; $number" | bc` #==> bc یک برنامه سودمند محاسبه کننده است. case "$dec" in [0-9]*) ;; #عدد مورد قبول است *) continue;; #در صورت خطا: صرفنظر esac #چاپ تمام تبدیلها در یک سطر. #==> here document فرمان را به bc تغذیه میکند. echo `bc <<! obase=16; "hex="; $dec obase=10; "dec="; $dec obase=8; "oct="; $dec obase=2; "bin="; $dec ! ` | sed -e 's: : :g' done } while [ $# -gt 0 ] #==>آیا واقعاً یک «حلقه while» در اینجا لازم است، چون تمام موارد #==>+ یا از حلقه خارج میشوند یا اسکریپت را خاتمه میدهند. #==> (توضیح فوق از Paulo Marcel Coelho Aragao است.) do case "$1" in --) shift; break;; -h) Usage;; #==>پیغام راهنمایی. -*) Usage;; *) break;; #عدد نخست esac #==> کنترل خطا جهت ورودی غیر مجاز، میتواند مناسب باشد. shift done if [ $# -gt 0 ] then PrintBases "$@" else #خواندن از ورودی استاندارد. while read line do PrintBases $line done fi exit
یک روش جایگزین برای فراخوانی bc، مستلزم استفاده از here document جاسازی شده در یک بلوک جایگزینی فرمان است. این روش، بویژه موقعی مناسب است که یک اسکریپت به عبور دادن لیستی از گزینهها و فرمانها به bc نیاز دارد.
variable=`bc << LIMIT_STRING options statements operations LIMIT_STRING ` ...or... variable=$(bc << LIMIT_STRING options statements operations LIMIT_STRING )
مثال 16-49. فراخوانی bc با استفاده از here document
#!/bin/bash #فراخوانی bc با استفاده از جایگزینی فرمان در تلفیق با یک here document var1=`bc << EOF 18.33 * 19.78 EOF ` echo $var1 # #نشانهگذاری $( ... ) نیز کار میکند. v1=23.53 v2=17.881 v3=83.501 v4=171.63 var2=$(bc << EOF scale = 4 a = ( $v1 + $v2 ) b = ( $v3 * $v4 ) a * b + 15.35 EOF ) echo $var2 # var3=$(bc -l << EOF scale = 9 s ( 1.7 ) EOF ) #سینوس 1.7 رادیان را برگشت میدهد. #گزینه -l کتابخانه ریاضی bc را احضار میکند. echo $var3 # #اکنون، آن را در یک تابع امتحان کنید... hypotenuse () #محاسبه وتر مثلث راست گوشه. { # hyp=$(bc -l << EOF scale = 9 sqrt ( $1 * $1 + $2 * $2 ) EOF ) #نمیتوان مقادیر ممیز شناور را به طور مستقیم از یک #تابع Bash برگشت داد، اما میتوان echo و ضبط نمود: echo "$hyp" } hyp=$(hypotenuse 3.68 7.31) echo "hypotenuse = $hyp" # exit 0
#!/bin/bash # #مولف: Mendel Cooper #اجازهنامه: Public Domain #نگارش 2.2، تاریخ انتشار 13oct08 #این یک نمونه بسیار ساده از شبیهسازی «Monte Carlo» است: #+یک مدل ریاضی از یک رویداد واقعی، با استفاده از اعداد #+شبهتصادفی برای تقلید کردن احتمال تصادف. #یک قواره زمین خشک کاملاً چهارگوش ، به ضلع 10000 واحد را در نظر بگیرید. #این زمین دارای یک استخر کاملاً دایرهای در مرکزش با قطر 10000 واحد است. #در حقیقت غیر از خشکی در چهار گوشه، این زمین بیشتر آب است. #(آن را به صورت یک چهارگوشه با دایرهای محاط شده در آن تصور نمایید.) # #ما گلولههای آهنی را از یک توپ مدل قدیمی به طرف مربع شلیک خواهیم نمود. #تمام گلولهها به جایی در مربع اصابت میکنند، در استخر، یا در گوشههای خشک. #چون بیشترین سطح را استخر گرفته است، اکثر گلولهها (SPLASH) در آب خواهند #افتاد، فقط اندکی از گلولهها با صدای خفه (THUD) در زمین سفت در چهار گوشه #+مربع فرود خواهند آمد. # #اگر ما شلیکهای بدون هدف و به اندازه کافی تصادفی به طرف مربع داشتهباشیم، #+آنوقت نسبت SPLASHها به کل پرتابها تقریباً مقدار PI/4 خواهد بود. ## مترجم: پرتابهایی قابل قبول هستند و شمارش میشوند که در محدوده مربع فرود آمده باشند. تعریف ساده شده، آن است که توپ در عمل فقط به طرف یک چهارم سمت راست بالای #+چهارگوشه، یعنی ربع اول سطح در مختصات دکارتی، شلیک میکند. تصویر توسط مترجم از ویکیپدیا اضافه گردیده است #از لحاظ نظری، پرتابهای مورد قبول بیشتر، اندازه مناسبتر. اما، یک اسکریپت #پوسته، برخلاف یک زبان compile شوندهِ دارای حساب ممیز شناور درونی، نیازمند #+یک مقدار کوتاه آمدن است، که این کار، درجه دقت شبیهسازی را کاهش میدهد. DIMENSION=10000 #طول هر ضلع زمین. #حد اعداد صحیح تولید شده را نیز تعیین میکند. MAXSHOTS=1000 #شلیک گلولههای زیاد به این تعداد. #10000 یا بیشتر میتواند بهتر باشد، اماخیلی طول میکشد. PMULTIPLIER=4.0 #Scaling factor. declare -r M_PI=3.141592654 #مقدار واقعی PI با ۹ رقم اعشار، به منظور انجام مقایسه. get_random () { SEED=$(head -n 1 /dev/urandom | od -N 1 | awk '{ print $2 }') RANDOM=$SEED #از اسکریپت مثال seeding-random.sh let "rnum = $RANDOM % $DIMENSION" #محدوده کمتر از 10000. echo $rnum } distance= #تعریف متغیر سراسری. hypotenuse () #محاسبه وتر مثلث راست گوشه. { #از مثال «alt-bc.sh». distance=$(bc -l << EOF scale = 0 sqrt ( $1 * $1 + $2 * $2 ) EOF ) #تنظیم scale به صفر، نتیجه را به عدد صحیح پایین گرد میکند، #+که در این اسکریپت، یک کوتاه آمدن ضروری است. #این کار، دقت و صحت این شبیهسازی را کاهش میدهد. } # # #بلوک کد "Main"، تقلید کردن از تابع main() زبان C است. #ارزشگذاری اولیه متغیرها. shots=0 splashes=0 thuds=0 Pi=0 error=0 while [ "$shots" -lt "$MAXSHOTS" ] #حلقه اصلی. do xCoord=$(get_random) #گرفتن X و Y تصادفی. yCoord=$(get_random) hypotenuse $xCoord $yCoord #distance = وتر مثلث. ((shots++)) printf "#%4d " $shots printf "Xc = %4d " $xCoord printf "Yc = %4d " $yCoord printf "Distance = %5d " $distance #فاصله از مرکز استخر #+مبداء مختصات (0,0). if [ "$distance" -le "$DIMENSION" ] then echo -n "SPLASH! " ((splashes++)) else echo -n "THUD! " ((thuds++)) fi Pi=$(echo "scale=9; $PMULTIPLIER*$splashes/$shots" | bc) #ضرب کردن در 4.0 echo -n "PI ~ $Pi" echo done echo echo "After $shots shots, PI looks like approximately $Pi" #به دلیل خطای گِرد کردن و غیرایدهآل بودن تصادف در $RANDOM، #+احنمالاً مقداری به سمت بالا میل میکند. #اما باز هم معمولاً در حدود به اضافه یا منهای 5% . . . #+یک تقریب غیر دقیق نسبتاً خوب. error=$(echo "scale=9; $Pi - $M_PI" | bc) pct_error=$(echo "scale=2; 100.0 * $error / $M_PI" | bc) echo -n "Deviation from mathematical value of PI = $error" echo " ($pct_error% error)" echo #انتهای قطعه کد main # # exit 0 #شاید کسی تعجب کند که آیا یک اسکریپت پوسته برای یک برنامهکاربردی #+پیچیده و دارای محاسبههای فراوان همچون یک شبیهسازی، مناسب است. # #برای این کار حداقل دو توجیه وجود دارد. #(1 به عنوان یک دلیل اجرایی: جهت نمایش آن که میتواند انجام بشود. #(2 برای نمونه اولیه و بررسی الگوریتمها، قبل از بازنویسی آن در یک #+زبان سطح بالای ترجمه شونده.
همچنین مثال A-37 را مشاهده نمایید.
برنامه سودمند dc (desk calculator) پشته گرا است و RPN یا (Reverse Polish Notation) یعنی نشانهگذاری لهستانی معکوس [1] را به کار میبرد. مانند bc، مقدار زیادی از قدرت یک زبان برنامهنویسی را در خود دارد.
مشابه شیوه کار با bc، به dc یک رشته فرمان را echo کنید.
echo "[Printing a string ... ]P" | dc #فرمان P رشته میان براکتهای مقدم را چاپ میکند. #و اکنون برای برخی محاسبات ساده. echo "7 8 * p" | dc # #7 و 8 را در پشته جای میدهد، ضرب میکند (عملگر *) #+سپس نتیجه را چاپ (عملگر p) میکند.
بیشتر افراد به علت ورودی غیر شهودی و عملگرهای نسبتاً مبهم dc از آن پرهیز میکنند. با این حال موارد استفاده خودش را دارد.
مثال 16-51. تبدیل یک عدد دسیمال به هگزادسیمال
#!/bin/bash # E_NOARGS=85 # BASE=16 # if [ -z "$1" ] then # echo "Usage: $0 number" exit $E_NOARGS fi #تمرین: کنترل صحت شناسه را اضافه کنید. hexcvt () { if [ -z "$1" ] then echo 0 return #برگشت دادن 0 اگر شناسه به تابع رد نشده باشد. fi echo ""$1" "$BASE" o p" | dc #o مبنای (پایه عددی) خروجی را تنظیم میکند #p بالای پشته را چاپ میکند #برای سایر گزینهها: «man dc» ... return } hexcvt "$1" exit
مطالعه صفحه info برای dc یک روش آزاردهنده برای فهمیدن ریزهکاریهای آن است. به نظر میرسد گروه کوچک خاصی از نابغههای dc باشند که از نمایاندن تسلط خود بر این برنامه نیرومند اما محرمانه لذت ببرند.
bash$ echo "16i[q]sa[ln0=aln100%Pln100/snlbx]sbA0D68736142snlbxq" | dc Bash
dc <<< 10k5v1+2/p # #تغذیه عملگرها به dc با استفاده از یک Here String #جای دادن 10 و قرار دادن آن به عنوان دقت اعشار(10k) #جای دادن 5 و جذر گرفتن از آن (5v، v یعنی ریشه دوم) #جای دادن 1 و افزودن آن به نتیجه به دست آمده (1+) #جای دادن 2 و تقسیم کردن نتیجه حاصل بر آن (2/) #برداشتن نتیجه از بالای پشته و چاپ کردن آن (p) #نتیجه برابر با 1.6180339887 است که اتفاقاً نسبت طلایی فیثاغورثی #با ده رقم اعشار است.
#!/bin/bash # MIN=2 #برای اعداد کوچکتر از این کار نمیکند. E_NOARGS=85 E_TOOSMALL=86 if [ -z $1 ] then echo "Usage: $0 number" exit $E_NOARGS fi if [ "$1" -lt "$MIN" ] then echo "Number to factor must be $MIN or greater." exit $E_TOOSMALL fi #تمرین: کنترل نوع را (برای عدم پذیرش شناسه غیر عدد صحیح) اضافه کنید. echo "Factors of $1:" # echo "$1[p]s2[lip/dli%0=1dvsr]s12sid2%0=13sidvsr[dli%0=\ 1lrli2+dsi!>.]ds.xd1<2" | dc # #کد فوق توسط Michel Charpentier <charpov@cs.unh.edu> نوشته شده است #(به عنوان کد یک سطری که در اینجا برای نمایش به دو سطر شکسته شده است. #در راهنمای ABS با مجوز استفاده گردیده است (تشکر!). exitbash$ factr.sh 270138 2 3 11 4093
اما یک روش دیگر انجام حساب ممیز شناور در یک اسکریپت، استفاده از توابع حساب توکار awk در یک shell wrapper است.
مثال 16-53. محاسبه وتر مثلث راست گوشه
#!/bin/bash # # ARGS=2 # E_BADARGS=85 # if [ $# -ne "$ARGS" ] # then echo "Usage: `basename $0` side_1 side_2" exit $E_BADARGS fi AWKSCRIPT=' { printf( "%3.7f\n", sqrt($1*$1 + $2*$2) ) } ' #فرمان(ها)-پارامترهای عبور داده شده به awk #اکنون، لولهکشی پارامترها به awk echo -n "Hypotenuse of $1 and $2 = " echo $1 $2 | awk "$AWKSCRIPT" # #یک echo-and-pipe روش آسانی برای تحویل دادن پارامترها به awk است. exit #تمرین: این اسکریپت را با استفاده از bc به جای awk بازنویسی نمایید. #کدام روش بیشتر قابل فهم است؟
[1] |
|