פרק 2: סוגי משתנים והגדרתם

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

2.1 כללי

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

2.2 הגדרה ספונטנית

הגדרה ספונטנית היא הגדרת משתנה תוך כדי קוד. אם באמצע הקוד תכתוב את השורה:

$m=5;

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

use strict;

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

2.3 הגדרת משתנה מקומי

זה לא טוב להמציא משתנה באמצע קוד. אז איך מצהירים על משתנה? פשוט:

my $m;

זוהי הצהרה רגילה של סקלר. כמו כל שורת int i ב-C. והיא גם מצייתת לכללים זהים. אם תשים את השורה בתוכנית הראשית, (ולא בתוך בלוק כלשהו) אזי זהו משתנה גלובלי. אם תשים את המשתנה בתוך בלוק, אזי הוא יושמד בסוף הבלוק.
עוד דוגמאות:
דוגמא להגדרה עם הצבה:

my $m=5;

דוגמא להגדרה של כמה משתנים בבת אחת:

my ($m, $s, $gfsd);

דוגמא בה מוגדרים שני משתנים, באחד מוצב הערך 5, בשני מוצבת מחרוזת.

my ($m, $s)=(5,"Moshe");

שים לב שכל שורה מסתיימת בנקודה-פסיק. (;) נקודה-פסיק מסמנת סוף שורה\פקודה בפרל, בדיוק כמו ב-C.

2.4 משתנים סקלריים

בשפות תוכנה אחרות, יש כל מיני סוגי משתנים. שלמים, נקודה-צפה, בוליאניים, תווים ומחרוזות. בפרל קיים רק את הסקלר. (Scalar) הסקלר מכיל את כל המספרים, ואת כל המחרוזות, לא משנה באיזה אורך או רמת דיוק. הסקלר מאופיין ע"י סימן הדולר ($) בתחילתו. דוגמאות:

$m=5; 
$m2="Have a nice day"; 
$zfdg='a'; 
$fnlpi=3.14157265; 
$stam="7"; 
$van=$stam+$m;

שימו לב לאחרון: אני מחבר את המחרוזת "7" עם המספר 5. מה שקורה זה תרגום אוטומטי. אם מנסים להפעיל פעולה אריתמטית על מחרוזת, פרל בודק מה יש בפנים. אם המחרוזת היא בעצם מספר, אזי היא מתורגמת למספר ועליו מתבצעת הפעולה. אם המחרוזת מכילה גם מספר וגם תוים אחריו, פרל בשקט (אלא אם כן השתמשת ב-"-w"!) יוריד את התוים וישאיר את המספר. אם המחרוזת מכילה תוים בלבד, היא תתורגם לערך 0 (אפס).
לייצוג ערכים בוליאניים, בפרל 0 זה שקר, כל דבר אחר זה אמת. מחרוזות - מחרוזת ריקה זה שקר, כל מחרוזת מלאה אחרת זה אמת.
הערה - המחרוזת "0" היא שקר, כיוון שהיא מתורגמת למספר 0, שהוא שקר. אפשר לעקוף את זה, ולהוציא את המחרוזת "0 ". שים לב לרווח לפני האפס. הרווח מונע מהמחרוזת להיתרגם אוטומטית לאפס, ובבדיקה בוליאנית המחרוזת תהיה שווה לאמת. אם תפעילו על המחרוזת פעולה אריתמטית, המחרוזת תיתרגם לאפס. ככה מוציאים אפס-אמת.
דוגמא לחתיכת קוד חסרת תועלת:

#!/usr/bin/perl -w 
use strict; 
my ($a,$b,$c,$d)=(1,2,3); 
$d = $a+$b*$c/$a; 
print "And the number is: $d !\n";

כמו שאתם כבר מנחשים, כל הפעולות האריתמטיות הרגילות פועלות כרגיל.
הערה - בקטע הקוד הצבנו ערך לתוך $d אחרי ההצהרה. מה היה בתוך $d אחרי ההצהרה ולפני ההצבה?
התשובה היא, שאז היה בתוך $d את הערך undef, או בעברית, לא-מוגדר. זו הדרך של פרל להגן על המתכנת מפני השתמשות במשתנה שלא אותחל. אם תנסה לעשות פעולה כלשהי על undef, תקבל הודעת שגיאה. מצד שני, אפשר להציב undef לתוך סקלר, ואפשר לבדוק האם הערך שבתוך סקלר מוגדר, בעזרת הפונקציה defined.
חתיכת קוד:

#!/usr/bin/perl -w 
use strict; 
my $m; 
if (defined($m)) { 
    print $m; 
} else { 
    print "Undefined!"; 
}

לבסוף, שים לב שגם undef בבדיקה בוליאנית הוא שקר.

אופרטורים אריתמטיים:
תוצאהשםאופרטור
2 + 3 = 5חיבור$a + $b
4 - 3 = 1 חיסור$a - $b
2 * 3 = 6 כפל $a * $b
17 % 3 = 2 מודולו (שארית)$a % $b
8 / 4 = 2 חילוק $a / $b
2 ** 3 = 8 חזקה $a ** $b
$a = $a -1 הפחתה-עצמית $a--, --$a
$a = $a +1 ייסוף-עצמי $a++, ++$a

2.5 מערכים

אוקי, יש לנו משתנים סקלרים. עכשיו צריך לאגד אותם למערכים. המערך מסומן בשטרודל בתחילתו, (@) ומכיל כל כמות של משתנים סקלרים, ורק משתנים סקלרים. אם תנסה להכניס מערך לתוך מערך, תקבל מערך אחד ארוך. (אפשר להכניס דברים אחרים לתוך מערך, אבל באמצעות מצביעים - מה ששייך לספר אחר)

my @array=(4,5,7,3,3,71,"Samba!",undef,3.14,$m,@old_array,83);

מה שיצא מהדוגמא הזאת, זה מערך אחד גדול, המכיל את מספרים, מחרוזות, ערך שבתוך $m כל הערכים שבתוך @old_array ואפילו undef אחד.
מערכים בפרל הם בעלי גודל משתנה - כלומר, אפשר להוסיף לסופם, לתחילתם או לכל מקום באמצע איברים נוספים, ואפשר גם להוציא איברים.

my @arr = (1,2,3); 
my $avar = 5; 
push (@arr, $avar++); # Add to the end. @arr=(1,2,3,5); 
unshift (@arr, $avar++); # Add to the beginning. @arr=(6,1,2,3,5); 
$avar = pop (@arr); # Remove from the end. @arr=(6,1,2,3), $avar=5. 
$avar = shift(@arr); # Remove from the beginning. @arr=(1,2,3), $avar=6

שים לב שהסימן '#' מציין הערה, הנמשכת עד סוף השורה. (שלא כמו ב-C, פה הערות לא יכולות להפרס על כמה שורות, אלא כל הערה היא שורה בודדת)
גישה לערכים בתוך מערך: אפשר לגשת לאיברי המערך בבודדים, או לקבוצה שלמה, בצורה של תת-מערך.

$arr[3]=7; 
@arr[4,5]=(32,34);

שים לב שבדוגמא הראשונה, $arr[3] מתחיל עם סימן דולר, ולא שטרודל. זה כיוון שבדוגמא זו אנו מתייחסים לאיבר ספציפי בתוך המערך, שהוא בפני עצמו סקלר, ולכן הוא מתחיל בסימן דולר. בדוגמא השניה, אני מתחיל בסימן השטרודל, כיוון שאני מתייחס לחתיכה מהמערך, שהיא מערך קטן בפני עצמה, ולכן מגיע לה שטרודל.
אני רוצה להזכיר כאן דוגמא מהסעיף הקודם:

my ($a,$b,$c,$d)=(1,2,3);

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

my ($a,@b,$c,$d)=(1,2,3);

הפעם @b הוא מערך, ולא סקלר. מה שיקרה הפעם זה:$a=1, @b=(2,3) ו-$c=$d=undef. זה קורה בגלל ש-@b הוא מערך, ולכן ההצבה מכניסה אליו את כל האיברים שנותרו, ולא משאירה ל-$c, $d איברים.
דוגמא אחרת:

$a=@arr;

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

$size = scalar(@arr);
$size = $#arr +1;

בשתי השורות $size יקבל את אותו הערך כמו בדוגמא הקודמת. שים לב לתוספת 1 בשורה השניה. זה כי "$#arr" נותן את מספר האיבר האחרון המערך. ומכיוון שהמיספור מתחיל מאפס, אז צריך להוסיף 1 כדי להגיע למספר האיברים במערך. מה יקרה אם המערך ריק? תנסה לבד.
הערה – בעמוד הקודם ציינו שהסימן '#' מסמל תחילת הערה. בכל זאת זה משמש גם לגודל מערך. תחייה עם זה.
ככה שולפים את האיבר האחרון במערך:

$a = $arr[$#arr];

לבסוף, פקודת splice לטיפול במערך. פקודה קצת מסובכת אך שימושית לעיתים, המסוגלת להחליף את pop, push וכל שאר הפקודות של הכנסת\הוצאת\החלפת איברים במערך. הנה דוגמאות לשימוש בפונקציה:

my @arr1 = (1,2,3,4,5); my @arr2=(6,7,8); my @arr3; 
@arr3=splice(@arr1,2,2,@arr2);

הפקודה הזאת מורידה את האיברים 3,4 מתוך @arr1, מכניסה במקומם את איברי @arr2, והאברים שהוסרו מוכנסים ל-@arr3 ומה שיוצא זה:

@arr1 = (1,2,6,7,8,5), @arr2=(6,7,8), @arr3=(3,4)

הכנסת והוצאת איבר בעזרת splice:

splice(@arr1,3,0,2.5); # @arr1=(1,2,6,2.5,7,8,5) 
my $val=splice(@arr1,5,1); # @arr1=(1,2,6,2.5,7,5), $val=8

רק נסביר, ש-0 מסמל לפני האיבר הראשון, 5 אחרי האיבר החמישי, 1- זה לפני האיבר האחרון.

2.6 טבלת האש

או בלעז, Hash-Table. זהו מבנה נתונים דומה למערך, רק שבמקום להשתמש במספר כדי לציין מקום של איבר במערך, לכל איבר יש מפתח, שהוא יכול להיות מחרוזת או מספר (אבל לא מצביע) שהוא המציין של האיבר. (שהוא סקלר כלשהו) לסוג נתונים זה יש את הסימן % בתחילת שם המשתנה.
דוגמא להגדרה של האש:

my %h=( 1 => "elf", "Zambura" => 5, 5=>7, "another"=>7);

זוהי טבלת האש עם ארבעה איברים, עם המפתחות (1,"Zambura",5,"another") ועם הערכים ("elf",5,7,7) כדי לקבל את הערכים הללו משתמשים בפקודות:

my @ar1=keys(%h); # @ar1 gets (1,"Zambura",5,"another") 
my @ar2=values(%h); # @ar2 gets ("elf",5,7,7)

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

$m=$h{"Zambura"}; 
@ar=@h{1, "another"};

וכדי להכניס איבר חדש:

$h{"new one"}=3.14;

אגב, כמו תמיד בטבלאות האש, המפתחות חייבים להיות ייחודיים. זה אומר, שאם בדוגמא למעלה אני אכתוב "another" במקום "new one", אזי הערך הישן שתחת another יידרס (שזה 7) ובמקומו יהיה 3.14.
בשביל מה כל הסיפור הזה טוב? האש משמש להרבה דברים, כגון שמירה על מאפיינים שונים. אפשר לשמור מאפיינים שונים באותו האש, וכל מי שצריך מאפיין כלשהו, ניגש אליו ישירות באמצעות השם שלו, בלי לגעת במאפיינים אחרים. עוד שימוש זה לפברק מבני מערכים (struct של שפת C) כמה שזה נשמע מפתיע, לא קיימים בפרל מבנים, וכדי בכל זאת לפברק אותם פשוט משתמשים בהאש.

2.7 הקשרים ומשתנים

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

$acla=localtime(); 
@arr=localtime();

שתי הפקודות עושות את אותו הדבר, רק שהראשונה מחזירה מחרוזת של זמן, שנראית ככה: "Thu Oct 13 04:54:34 1994" והשנייה מחזירה את המערך:

($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst)

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

@arr=("Today","is",scalar(localtime));

אם לא היינו שמים את ה-scalar, אזי localtime היתה רואה הקשר רשימה, ושופכת את כל המערך הגדול שלה. ככה קיבלנו רק את המספר.

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

מכיוון שעד כה כל מה שראינו היה סקלרים, מערכים והאשים, (אפילו לא מחרוזות!) אה, ואת הפקודה print, אז תסלח לי שהתוכנית קצת מטופשת. תתרגל.

#!/usr/bin/perl -w 
use strict; 
my ($a,$b,$c,$d)=(73,22,"Oops"); 
my (@arr1, @arr2, @arr3); 
my (%h1, %h2); 
@h1{"a","b","c"}=($a, $b, $c); 
@arr1=keys(%h1); 
@arr2=values(%h1); 
@arr3=%h1; 
push(@arr1, "klick"); 
push(@arr2, "klack"); 
%h2 = ("first"=>1, "second"=>2, "third"=>3, "forth"=>4); 
$h2{"fifth"}=5; 
splice(@arr3, 0, scalar(@arr1), @arr1); 
$h1{pop(@arr1)}=pop(@arr2); 
$c=7; 
$d = $a+$b*$c/$a; 
print "And the number is: $d !\n"; 
print "Hash table: ", %h2, "\n"; 
print "Keys: ", @arr1, " Values: ", @arr2, "\n";
[הקודם : מילות פתיחה][חזרה לתוכן העניינים][הבא : מבני בקרה]

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