רשימות

map#

החלת בלוק או ביטוי על כל איבר של רשימה והחזרת התוצאות בצורה משוטחת.

map מעריך את BLOCK (או EXPR) פעם אחת לכל איבר של LIST, כאשר $_ משמש ככינוי לאותו איבר, ואוסף את כל מה שכל הערכה מחזירה לרשימה משוטחת אחת. מכיוון שכל הערכה עשויה להניב אפס, אחד או הרבה ערכים, אורך הפלט אינו תלוי באורך הקלט - אותו שיטוח הוא העניין של map. בהקשר רשימה map מחזיר את הרשימה שנוצרה; בהקשר סקלר הוא מחזיר את מספר האיברים הכולל.

תקציר#

my @out = map { BLOCK } LIST;       # block form
my @out = map EXPR, LIST;           # expression form
my @out = map { f($_) } grep { ... } sort LIST;   # chained

מה מקבלים בחזרה#

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

  • החזרת ערך אחד לכל קלט ← טרנספורמציה (אותו אורך בפלט).

  • החזרת רשימה לכל קלט ← הרחבה של 1-ל-N (פלט ארוך יותר).

  • החזרת הרשימה הריקה () ← דילוג על קלט זה (פלט קצר יותר; האידיום map-כ־filter).

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

my @doubled = map { $_ * 2 }          1 .. 4;     # (2, 4, 6, 8)
my @words   = map { split /\s+/, $_ } @lines;     # 1-to-N
my @kept    = map { $_ > 0 ? $_ : () } @nums;     # filter
my $count   = map { $_ * $_ } @nums;              # scalar ctx

מצב גלובלי#

$_ משמש ככינוי לכל איבר של LIST, לא מועתק. שינוי של $_ בתוך הבלוק משנה את איבר המקור. זה לפעמים שימושי אך בדרך־כלל foot-gun - יש לפנות ללולאת foreach פשוטה כשמתכוונים לשנות במקום.

my @v = (1, 2, 3);
my @r = map { $_ *= 10; $_ } @v;
# @v is now (10, 20, 30) - the source was mutated

כינוי ללא־משתנה (ליטרל, ערך החזרה של פונקציה, איבר hash-slice שעדיין לא קיים) ולאחר מכן השמה ל־$_ נכשלים בזמן ריצה עם Modification of a read-only value. ראו $_.

map גם מבצע localise ל־$_ סביב הקריאה, כך ש־$_ חיצוני הנראה לקורא משוחזר כאשר map מחזיר.

דוגמאות#

טרנספורמציה - להפוך כל מילה לאותיות גדולות:

my @upper = map { uc } @words;

הרחבה 1-ל-N - פיצול כל שורה על רווח לבן לטוקנים שלה:

my @tokens = map { split /\s+/ } @lines;

אידיום filter - שמירה רק על ערכים אמיתיים מטרנספורמטיביים:

my @positives = map { $_ > 0 ? $_ : () } @numbers;

בניית האש מרשימה - כל הערכה מחזירה רשימה בת שני איברים:

my %seen = map { $_ => 1 } @keys;
my %idx  = map { ($keys[$_] => $_) } 0 .. $#keys;

שרשור מורכב - מיון, סינון, טרנספורמציה. map, grep, ו־sort לוקחים כולם LIST מימין, ולכן הם מצטרפים מימין־לשמאל:

my @top = map  { "$_->{name}: $_->{score}" }
          sort { $b->{score} <=> $a->{score} }
          grep { $_->{score} >= 50 }
          @records;

שיטוח מבנה - כל קלט תורם מספר משתנה של פלטים:

my @leaves = map { ref($_) eq 'ARRAY' ? @$_ : $_ } @mixed;

זיווג עם אינדקס באמצעות List::Util::pairs או משתנה אינדקס מפורש - map עצמו אינו חושף כזה:

my @numbered = do {
    my $i = 0;
    map { sprintf "%d. %s", ++$i, $_ } @items;
};

מקרי קצה#

  • $_ משמש ככינוי, לא מועתק. השמה ל־$_ כותבת בחזרה למקור. אם איבר המקור הוא קריאה־בלבד (קבוע, ערך החזרה של קריאת פונקציה ששוטחה לתוך LIST), ההשמה מעלה Modification of a read-only value. יש להתייחס ל־$_ כקלט־בלבד אלא אם רוצים במפורש את תופעת הלוואי.

  • map אינו לולאה. last, next, redo, ותוויות לולאה אינם חלים על map. הם מתייחסים ללולאה האמיתית העוטפת ויתנהגו בהתאם - בדרך־כלל לא מה שהמחבר התכוון. יש להשתמש ב־foreach כשנדרשת זרימת בקרת־לולאה.

  • הקשר void מנוטרל. map { ... } LIST; כשהתוצאה מושלכת מנפיק אזהרה Useless use of map in void context תחת use warnings. כשרוצים את תופעות הלוואי, לא את הרשימה, יש לכתוב foreach.

  • עמימות מפענח בין BLOCK ל־EXPR. { מתחיל גם בלוק וגם בנאי של הפניית האש. map { ... } LIST ו־map { ... }, LIST נראים זהים למפענח בסוגריים הפותחים; Perl מנחש על־סמך מה שהוא מוצא לאחר־מכן. בלוק שמתחיל במה שנראה כמפתח האש בתוספת => עשוי במיוחד להתפענח שגוי:

    my %h = map {  "\L$_" => 1  } @a;   # guessed EXPR, wrong
    my %h = map { +"\L$_" => 1  } @a;   # unary + forces BLOCK
    my %h = map {; "\L$_" => 1  } @a;   # leading ; forces BLOCK
    my %h = map { ($_, 1) }       @a;   # parens - unambiguous
    my %h = map { lc($_) => 1 }   @a;   # function call - unambiguous
    

    כדי להחזיר האש אנונימי לכל איבר, יש לאלץ את בנאי ה־hash-ref עם +{:

    my @hashes = map +{ lc($_) => 1 }, @array;
    
  • הסדר יציב. map מעריך משמאל־לימין ומצרף תוצאות משמאל־לימין. הרשימה שנוצרת שומרת את סדר הקלט, כאשר תרומת כל קלט מופיעה ברצף.

  • LIST ריקה. map מעריך את הבלוק אפס פעמים ומחזיר את הרשימה הריקה. הבלוק אינו מוערך עבור תופעות הלוואי שלו.

  • map מקונן. map פנימי רואה את $_ שלו; ה־$_ החיצוני מוסתר למשך ההערכה הפנימית. יש ללכוד את הערך החיצוני קודם אם שניהם נחוצים:

    my @pairs = map {
        my $outer = $_;
        map { [$outer, $_] } @inner
    } @outer;
    

הבדלים מ־upstream#

תואם מלא ל־upstream Perl 5.42.

ראו גם#

  • grep - אותה צורה כמו map, אך שומר איברים שהבלוק אמת עבורם במקום להמיר אותם; מתחבר באופן טבעי עם map ו־sort

  • sort - ממיין רשימה; נהוג להרכיב אותו עם map באמצעות הטרנספורם השוורצי (mapsortmap)

  • for / foreach - בשימוש כשרוצים איטרציה עבור תופעות לוואי, שינוי המקור, או בקרת־לולאה (last/next/redo)

  • x - אופרטור חזרה על רשימה; אורתוגונלי ל־map אך משמש לעיתים קרובות לבניית ה־LIST ש־map צורך

  • List::Util::reduce - קיפול רשימה לערך יחיד; להשתמש בו כאשר התוצאה אינה רשימה אלא צבירה (סכום, מקסימום, התאמה ראשונה)

  • $_ - כינוי האיבר בתוך הבלוק; קריאה־בלבד כאשר איבר המקור הוא ליטרל או ערך החזרה של פונקציה