פרק 3: מבני בקרה

[הקודם : סוגי משתנים והגדרתם][חזרה לתוכן העניינים][הבא : טיפול במחרוזות]

3.1 ביטויים בוליאניים

כמו שכבר הסברנו בפרק סוגי משתנים, סעיף משתנים סקלרים, בפרל 0, המחרוזת הריקה ("") ו-undef הם שקר, כל דבר אחר זה אמת.
פרל תומכת בכל האופרטורים הבוליאניים המוכרים, אם בסגנון C, (&&, ||, !) או בסגנון פסקל. (And, Or, Not)
אופרטורים לוגיים:
פעולהאופרטור פסקל אופרטור C
And$a and $b$a && $b
Or$a or $b$a || $b
Notnot $a! $a
אופרטורי השוואה: בפרל יש אופרטורים שונים להשוואה בין מספרים ולהשוואה בין מחרוזות. (טוב, בשפות אחרות כדי להשוות בין מחרוזות יש פונקציות מיוחדות)
פעולהאופרטור מחרוזתאופרטור מספר
האם שווה$a eq $b$a == $b
האם לא שווה$a ne $b$a != $b
האם גדול $a gt $b$a > $b
האם גדול-שווה$a ge $b$a >= $b
האם קטן $a lt $b$a < $b
האם קטן-שווה$a le $b$a <= $b
השוואה$a cmp $b$a <=> $b
אופרטור ההשוואה מחזיר: 1 אם $a>$b, מחזיר 0 אם $a==$b, ומחזיר -1 אם $a<$b.

3.2 פקודת if וניגזרותיה

כן, ניגזרותיה. כלל גדול בפרל אומר, "יש יותר מדרך אחת לעשות את זה". ובפרט, יש יותר מדרך אחת לעשות if. אבל נתחיל ברגילה, בסדר? הנה:

if (condition1) { 
    Set of commands...
} elsif (condition2) { 
    Set of commands...
} else { 
    Set of commands...
}

אלה שני משפטי תנאי רגילים, משורשרים.
יש גם וורסיות אחרות של משפט ה-if. הפקודות הבאות זהות:

if ($m==3) { 
    print "Avukado"; 
} 
print "Avukado" if ($m==3); 
print "Avukado" unless ($m != 3);

3.3 פקודת while

טוב, פה פרל זהה לשפת C. כמעט. הנה דוגמא:

while ($line=<>) { 
    $count_lines++;
    if (substr($line,1) eq '#') { 
        next; 
    } 
    if ($line eq "End\n") { 
        last; 
    } 
    print $line;
}

אז מה יש לנו כאן? בשורה הראשונה יש את אופרטור ה-<>. זה אופרטור שקורא שורה מה-stdin, ומציב אותה לתוך $line. אם לא נשארו שורות - הוא יחזיר undef, מה שיתן שקר, והלולאה תסתיים. נסביר בפרק מאוחר יותר על האופרטור הזה.
מיד אחרי שקראתי את השורה, אני סופר אותה. שיהיה. אח"כ אני בודק האם התו הראשון של השורה הוא "#". אם כן, אזי אני מדלג ועובר לשורה הבאה. אם השורה היא “End”, הסתיים הקובץ, ואני שובר החוצה מהלולאה. ובסוף, אם לא קרה שום דבר דרסטי, אני מדפיס את השורה לפלט הסטנדרטי (STDOUT).

3.4 לולאת for

לולאת for היא בהחלט כמו המקבילה של ב-C. כמעט.

my ($m, $i); 
$m=1; 
for ($i=1; $i<5; $i++) { 
    $m = $m*$i; 
}

יש ללולאות גם פקודות בקרה:

my ($m, $i, $j) = (0,0,0); 
for ($i=1, $m=1; $i<5; $i++) { 
    $j++; 
    redo unless ($j>=$i); 
    if ($i+$m>30) { 
        last; 
    } 
    next if ($i==1); 
    $m=$m*$i/$j; 
}

פקודת next מקפיצה את ההרצה לסוף הלולאה, ומתחילה סיבוב חדש של הלולאה. כלומר, $i מתקדם באחד, והלולאה שוב מתחילה לרוץ. פקודת last שוברת את הלולאה, והתוכנית ממשיכה לרוץ. פקודת redo היא פקודה מיוחדת. היא מקפיצה את ההרצה לתחילת הלולאה. זה אומר ש-$i לא מתקדם, אבל $j שממוקם שורה מתחתיו, כן יתקדם. לא ראיתי את זה בשום שפה אחרת, אבל דוגמא מעניינת להוראה תמצאו בפרק על טיפול בקבצים.
מקרה אחר של for היא ה-foreach. הנה דוגמא:

foreach $count (10,9,8,7,6,5,4,3,2,1,"BOOM!") { 
    print "$count\n"; 
}

בכל סיבוב של הלולאה, $count מקבל אחד מהערכים של הרשימה שאחריו, שמודפס בפקודה. אם הרשימה היא בעצם מערך, אזי $count בעצם נהפך לערך. לא נתמקד במוזרות הזאת, רק נראה דוגמא:

my @arr=(1,2,3,4,5); 
foreach $val (@arr) { 
    $val=$val*2+1; 
}

כאן הערכים בתוך המערך @arr ישתנו. מוזר, אך אמיתי ויעיל.

3.5 פעולות על תנאי

כשפרל בודק משפטים לוגיים, הוא עושה זאת בחלקים. לדוגמא, אם יש את הביטוי הבא:

$x or $m++

אם $x הוא אמת, אזי $m לא יקודם. זה בגלל שפרל כבר יודע שהערך של הביטוי הוא אמת, ואין לו צורך לבדוק את חלקו השני.
משתמשים בזה בפרל המון, פשוט במקום לעשות if. הנה פתיחת קובץ טיפוסית:

open(my $fh, ">", "zorba.txt") or die "Cann’t open that file!\n";

הפקודה open פותחת קובץ, בשם zorba.txt, תחת המשתנה $fh. אם היא מצליחה, היא מחזירה אמת, ואז פרל לא ממשיכה לבדוק את התנאי. אם open מחזירה שקר, אז פרל ממשיך לבדוק את התנאי, ומגיע לפקודה die "string", שמוציאה את הודעת השגיאה ועוצרת את ההרצה.
נכון, זה יכול להיות מכוער, ואפשר לכתוב בעזרת זה דברים לחלוטין בלתי קריאים. אבל זה פרל, ואפשר להשתמש בזה במקום המון פקודות if. תגיד בעצמך מה יותר קריא, הפקודה שלמעלה או פקודה מקבילה בסגנון C?

if (open(FH, ">", "zorba.txt")==0) { 
    die "Cann't open that file!\n"; 
}

בקיצור, אחרי שמתרגלים לזה, זה נחמד.

3.6 תוכנית דוגמא

כרגע יש לנו, כאבני בנין את המשתנים, ואת מבני הבקרה. התוכנית עכשיו יכולה להיות קצת יותר אינטילגנטית. (אבל לא בהרבה)

#!/usr/bin/perl -w 
use strict; 
my ($key, $val); 
my %h; 
while ($key=<>) { 
    defined($val=<>) or die "Odd number of line?!\n"; 
    chomp($key); 
    chomp($val); 
    $h{$key}=$val; 
} 
foreach $key (keys(%h)) { 
    print $key, "->", $h{$key}, ", "; 
} 
print "\n"; 
print $h{"message"}, "\n"; 
my $i ; 
for ($i=1; $i<$h{"loop count"}; $i++) { 
    last if ($h{"loop count"}*2-$i<5); 
    print $h{"loop msg"}, "\n"; 
}

התוכנית משתמשת בפקודה שעדיין לא הזכרנו - chomp. זוהי פקודה שמורידה מהקלט את סימני סוף השורה. אם לא נשים אותה, התוכנית גם תעבוד, אבל נקבל כל מיני אזהרות. (בגלל שאנחנו משתמשים ב-"-w" בשורה הראשונה של התוכנית, שאומרת לו להזהיר אותנו) זה יקרה בכל פעם שננסה להשתמש בקלט כמספר, ואז הוא צריך לתרגם אותו למספר, ישמיט את סימני סוף השורה, יזהיר אותנו שהוא עשה את זה, אבל ימשיך לרוץ.
מה שהתוכנית עושה זה לקרוא קובץ, וכל זוג שורות להכניס בתור מפתח וערך לתוך טבלת האש. אח"כ היא מדפיסה את טבלת ההאש שהתקבלה, מדפיסה את הערך שתחת המפתח "message", ואז רצה בלולאה לפי "loop count" ומדפיסה את "loop msg". קובץ קלט לדוגמא:

<Start input file> 
5
Oops.
loop count
17
And the winner is
me.
loop msg
AhAhAhAh!
message
Resistance is futile
<End input file>

שים לב שאין חשיבות לסדר הזוגות. ממילא הכל נשפך אח"כ לתוך האש ומתערבל שם. שים גם לב לכך שמספר השורות צריך להיות זוגי. אם הוא לא זוגי, (יכול להיות שהעורך שלך אוהב להוסיף שורה ריקה בסוף הקובץ) אז פשוט תוסיף בעצמך שורה ריקה, וכך יהיו לך שתי שורות ריקות בסוף הקובץ.
הרצת התוכנית: אם נקרא לקובץ הקלט פשוט "in3", לקובץ של הסקריפט "chap3.pl", אזי ביוניקס השורה הבאה תפעיל את הקובץ:

./chap3.pl <in3

בחלונות, בהנחה שפרל היא בנתיב החיפוש:

perl chap3.pl <in3

בהצלחה.

[הקודם : סוגי משתנים והגדרתם][חזרה לתוכן העניינים][הבא : טיפול במחרוזות]

נכתב ע"י שמואל פומברג, כל הזכויות שמורות © ראה פרק 1.5 לתנאי רשיון