מערכים#

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

my @days = ('Mon', 'Tue', 'Wed', 'Thu', 'Fri');

@days             # the whole array (5 elements)
$days[0]          # the first element - a single scalar
@days[1, 3]       # a slice - a list of two scalars
$#days            # the last valid index - here, 4
scalar @days      # the length - here, 5

אינדקסים מתחילים ב־0. אינדקסים שליליים נספרים מהסוף ($days[-1] הוא 'Fri').

אורך: $#arr מול scalar @arr#

שתי כמויות קשורות, המשמשות למשימות שונות:

$#days            # last valid index            - 4
scalar @days      # number of elements          - 5
$#days + 1        # equivalent to scalar @days

השמה ל־$#arr משנה את גודל המערך. כיווץ הורס את האלמנטים שמעבר לקצה החדש; הגדלה ממלאת אותם ב־undef:

my @a = (1, 2, 3, 4, 5);
$#a = 2;             # @a is now (1, 2, 3)         - length 3
$#a = 5;             # @a is now (1, 2, 3, undef, undef, undef)
$#a = -1;            # @a is now ()                - empty

הצורה @a = () היא השמת המערך־הריק הניבית; $#a = -1 שקולה ולעיתים ברורה יותר כאשר הכוונה היא ”shrink to nothing without changing the variable’s identity“.

גישה לאלמנטים#

my $first = $days[0];            # 'Mon'
my $last  = $days[-1];           # 'Fri'
my $last  = $days[$#days];        # same; redundant but explicit
$days[2]  = 'Wednesday';         # set
$days[10] = 'Sunday';            # auto-extends; $days[5..9] become undef

קריאה מעבר לקצה מחזירה undef (ללא אזהרה - קריאה מחוץ לטווח היא אי־אירוע). כתיבה מעבר לקצה מגדילה את המערך; החריצים שביניהם הופכים ל־undef.

my @a;
$a[5] = 'fifth';
# @a is now (undef, undef, undef, undef, undef, 'fifth')
print scalar @a;                  # 6

קריאת אינדקס שלילי רחוק יותר לאחור מההתחלה היא שגיאה קטלנית:

my @a = (1, 2, 3);
$a[-10];                          # FATAL: Modification of non-creatable
                                  #        array value attempted

פעולות המערך־כולו#

push    @arr, $value;             # append at end           - returns new length
my $v = pop  @arr;                # remove from end         - returns removed element
my $v = shift @arr;               # remove from start       - returns removed element
unshift @arr, $value;             # prepend                 - returns new length

splice  @arr, $offset, $len, @replacement;   # general remove/insert

shift ו־unshift הם O(n) משום שהם ממספרים מחדש כל אלמנט נותר; push ו־pop הם O(1) ממוצע. עבור עומסי עבודה בצורת תור שבהם זה חשוב, העדיפו push/shift בקצה הקצר או השתמשו ב־deque מ־List::Util (שיש לו רדוקציות, לא deque).

splice הוא אולר השוויצרי: הסירו מקטע של אלמנטים, הכניסו מקטע במקומם, החזירו את מה שהוסר:

my @arr = (1, 2, 3, 4, 5);
my @gone = splice @arr, 1, 2;            # @gone = (2, 3); @arr = (1, 4, 5)
splice @arr, 1, 0, ('a', 'b');            # insert at offset 1, no removal
                                          # @arr = (1, 'a', 'b', 4, 5)

$offset שלילי נספר מהסוף. $len שלילי שומר על מספר זה של אלמנטים גוררים:

splice @arr, -1;                  # remove just the last element
splice @arr, 0, -2;               # remove all but the last two

פרוסות#

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

my @arr = ('a', 'b', 'c', 'd', 'e');

my @subset = @arr[0, 2, 4];          # ('a', 'c', 'e')
my @middle = @arr[1..3];             # ('b', 'c', 'd')
my @last3  = @arr[-3..-1];           # ('c', 'd', 'e')
my @rev    = @arr[reverse 0..$#arr];  # ('e', 'd', 'c', 'b', 'a')

@arr[1, 3] = ('B', 'D');              # set positions 1 and 3
@arr[1, 3] = @arr[3, 1];              # swap two elements (no temp)

פרוסת מפתח/ערך עם % מחזירה זוגות אינדקס/ערך (Perl 5.20+):

my @arr = ('a', 'b', 'c', 'd', 'e');
my %picked = %arr[1, 3];             # (1 => 'b', 3 => 'd')

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

הקשר רשימה מול הקשר סקלר#

מערך בהקשר רשימה מניב את האלמנטים שלו; בהקשר סקלר, הוא מניב את אורכו:

my @arr = (10, 20, 30);

my @copy   = @arr;                # list context - three elements
my $length = @arr;                # scalar context - 3
print "got @arr\n";               # list context inside ""    - "got 10 20 30"
print "got " . @arr . "\n";       # scalar context (concat)   - "got 3"

ההבחנה היא מלכודת Perl הנפוצה ביותר. שני כללי אצבע:

  • בתוך מחרוזות במרכאות כפולות, מערכים משובצים כאלמנטים שלהם המחוברים על־ידי $" (רווח יחיד כברירת מחדל). ראו שיבוץ.

  • בהקשר אופרטור מספרי או מחרוזת (+, ., השוואה), מערך מניב את אורכו.

אלצו הקשר סקלר עם scalar כאשר מיקום הביטוי דו־משמעי אחרת:

print "got " . scalar(@arr) . " items\n";    # "got 3 items"
print "got @arr items\n";                    # "got 10 20 30 items" - different!

השמת רשימה וניב הספירה#

השמת רשימה למערך הופכת את המערך ארוך בדיוק כך:

my @arr = (1, 2, 3);
@arr = (10, 20);                  # @arr is now (10, 20)
@arr = ();                        # @arr is now empty

השמת רשימה לרשימה של סקלרים מפרקת (כל עודף מושלך; החסרים הופכים ל־undef):

my ($x, $y, $z) = (1, 2, 3);              # 1, 2, 3
my ($x, $y, $z) = (1, 2);                 # 1, 2, undef
my ($x, $y)     = (1, 2, 3);              # 1, 2 - '3' silently dropped

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

my $count = () = $string =~ /\d+/g;
# $string =~ /\d+/g  - match in list context, returns all matches
# () = LIST          - list assignment to empty list
# $count = SCALAR    - that assignment in scalar context = count of RHS

האמצע () = LIST הוא הטריק. בלעדיו, $count = $s =~ /\d+/g נותן בוליאני במקום ספירה.

הפניות מערך#

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

my @items = (1, 2, 3);

my $aref = \@items;               # reference to the existing @items
my $anon = [1, 2, 3];             # anonymous array reference

$aref->[0]                         # access through the arrow - 1
@{$aref}                           # fully bracketed deref - list (1, 2, 3)
@$aref                             # unbracketed deref     - same as above
$#{$aref}                          # last index of dereffed array
scalar @$aref                      # length

ראו הפניות לתמונה המלאה.

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

my @words = qw(the quick brown fox jumps over the lazy dog);

my @hist;
$hist[length $_]++ for @words;

for my $len (1 .. $#hist) {
    next unless $hist[$len];
    printf "%2d-letter words: %d\n", $len, $hist[$len];
}
# 3-letter words: 4
# 4-letter words: 2
# 5-letter words: 3

שימו לב ש־$hist[length $_]++ עושה autovivify (יוצר) לחריץ כשזו המילה הראשונה באורך זה. קריאה מ־$hist[7] מאוחר יותר מחזירה undef, ש־++ משמח להגדיל - אך next unless $hist[$len] מדלג על הפער לפני ההדפסה.

ראו גם#