שיבוץ#

מחרוזות במרכאות כפולות, here-docs, תבניות regex, ההחלפה s/.../.../ ו־qq// כולם משבצים: הם מרחיבים משתנים ורצפי escape מסוימים במקום. מחרוזות במרכאות יחידות, q// ותבנית הצד־השמאלי של tr/.../.../ אינם. ידיעה אילו מבנים משבצים ואילו צורות בדיוק מורחבות היא אחד הפריטים השימושיים ביותר בידע Perl.

my $name = "Alice";
my @nums = (1, 2, 3);

"hello, $name"              # "hello, Alice"
'hello, $name'              # 'hello, $name' - single quotes don't interpolate
"@nums in a string"         # "1 2 3 in a string"
"\$name is $name"           # "$name is Alice"

מה משבץ#

בתוך "" (וההקשרים הדמויי־מרכאות־כפולות האחרים), Perl מרחיב בדיוק שתי צורות של משתנה:

$name              # scalar - the simple form
@list              # array  - joined by $" (one space by default)

בתוספת צורות המנוי בסוגריים־מרובעים־ומסולסלים המורכבות על גביהן:

$arr[0]            # one element of @arr
$h{key}            # one value from %h
$ref->[0]          # via array ref
$ref->{key}        # via hash ref
@arr[1, 2]         # array slice
@h{qw(a b)}        # hash slice

שימו לב שהאשים שלמים אינם משבצים:

"hash is %h"       # "hash is %h" - literal '%' is fine, '%h' is NOT magical

אם אתם רוצים האש מוטל לתוך מחרוזת, פרמטו אותו במפורש עם Data::Dumper או עם join ידני.

מה לא משבץ#

&sub               # function calls don't interpolate
sub_with_args($x)  # neither do calls with arguments
$h->method         # method calls don't interpolate
$obj->{a} + 1      # arithmetic doesn't interpolate
$arr[0+1]          # this DOES interpolate as $arr[0+1] - subscripts allow expressions

"\u\L$name"        # case-modifying escapes - these DO interpolate
"\x{2603}"         # numeric escapes - DO interpolate (a snowman)

כלל האצבע: משתנים והמנויים שלהם משבצים; קריאות פונקציה/מתודה וחשבון על גביהם אינם. כשאתם רוצים ביטוי משובץ, ראו את המבנים @{[ ... ]} ו־${\ ... } למטה.

הסוגריים ${name}#

סוגריים מסולסלים סביב שם משתנה מבהירים היכן השם מסתיים:

my $who = "Larry";

"${who}speak"          # "Larryspeak"
"$whospeak"             # "" with warning: $whospeak is uninitialised
"${who}::sub"           # "Larry::sub"
"$who::sub"             # the variable $who::sub from package who - bug

ללא הסוגריים, Perl חמדן: $whospeak נקרא כמזהה אחד, $who::sub נקרא כשם מוסמך־חבילה. הסוגריים מאלצים את הפרסר לעצור ב־}.

זו אינה אותה צורת־סוגריים כמו מנוי האש:

"$h{key}"              # hash element                 - what most people mean
"${h}{key}"            # hash element in a different parse path - same result
"${h{key}}"            # also the same
"${\(\$h{key})}"        # the absurd "interpolate an expression" form

הסוגריים ${name} משמשים בעיקר למקרה ההבהרה (שם שאחריו תווים אלפאנומריים שאחרת היו נאכלים), לא לגישת האש.

הצורות @{...} ו־${...} של deref־עם־סוגריים#

כשאתם רוצים לבצע dereference למשהו בתוך מחרוזת, צורת הסוגריים מאפשרת לביטוי ה־deref להיות שרירותי:

my $config = { users => ['Alice', 'Bob'] };

"users: @{ $config->{users} }"     # "users: Alice Bob"
"users: $config->{users}"          # "users: ARRAY(0x...)" - wrong; whole arrayref stringified
"users: @$config->{users}"         # parse error / wrong

The @{ EXPR } form deref’s EXPR as an array. The braces are required because the deref target is an expression, not a plain variable. @$config->{users} doesn’t parse the way intended

  • @$config would deref $config as an array, and ->{users} is a hash-element access on what’s left.

אותו דבר עבור ${ EXPR } (deref סקלר) ו־%{ EXPR } (deref ההאש, המחזיר רשימה שטוחה, שימושי לעיתים רחוקות בשיבוץ).

@{[ expr ]} - שבצו כל ביטוי רשימה#

כדי לשבץ את תוצאת ביטוי שרירותי, עטפו אותו ב־@{[ ... ]}:

my $name = "World";
print "hello, @{[ uc $name ]}!\n";              # "hello, WORLD!"

print "found @{[ scalar @hits ]} matches\n";     # interpolate a count

print "users: @{[ join ', ', @users ]}\n";       # interpolate a join

המנגנון: [ ... ] בונה הפניית מערך אנונימית שתוכנה הוא ערך הקשר־הרשימה של הביטוי; @{ ... } אז עושה deref ל־arrayref הזה בחזרה לרשימה, המשתבצת כפי שרשימה משתבצת (מחוברת על־ידי $").

זהו העקיפה הקנונית עבור ”I want a function call inside a double-quoted string“. החלופה היא שרשור:

print "hello, " . uc($name) . "!\n";             # equivalent

הצורה @{[ ... ]} צפופה יותר כשיש מספר שיבוצים; שרשור ברור יותר כשיש רק אחד. בתבניות עם שדות רבים, printf או sprintf בדרך כלל ברורים עוד יותר:

printf "found %d matches\n", scalar @hits;

השתמשו ב־@{[ ... ]} כשאתם באמת רוצים ביטוי Perl באמצע מחרוזת והחלופות מכוערות יותר.

${\ expr } - שבצו ביטוי סקלר#

המקבילה הסקלרית של @{[ ... ]}:

print "answer: ${\ 6 * 7 }\n";                   # "answer: 42"
print "ts: ${\ scalar localtime }\n";             # "ts: Mon Apr 27 ..."

\ EXPR יוצר הפניה לערך הסקלרי של EXPR; ${ ... } עושה לו deref. התוצאה היא ערך אחד, המשובץ כסקלר.

ברוב המקרים הצורה @{[ ... ]} אחידה יותר - היא עובדת עבור הקשר סקלר ורשימה כאחד. ${\ ... } ברורה לעיתים כשאתם רוצים ספציפית הקשר סקלר:

"got @{[ items() ]} items"      # items() called in LIST context
"got ${\ items() } items"        # items() called in SCALAR context

דו־משמעות מנוי־האש בתבניות#

בתוך תבנית regex (שהיא דמוית־מרכאות־כפולות), הפרסר צריך לנחש אם $foo[bar] פירושו ”interpolate $foo[bar] (one element of @foo)“ או ”interpolate $foo and then match a character class [bar]“:

my @foo = ('hi');
$x =~ /$foo[abc]/                 # @foo exists - parsed as $foo[abc] (one element)
$x =~ /$foo[abc]/                 # @foo doesn't exist - parsed as $foo + char class

ההבהרה היא צורת הסוגריים:

$x =~ /${foo}[abc]/               # $foo + character class [abc]
$x =~ /$foo[5]/                   # element 5 of @foo
$x =~ /\Q$foo\E[abc]/             # $foo (literal) + character class

המצטט \Q...\E משבית פרשנות מטה־תווים בתוך תחומו, מה שהוא התיקון הנקי ביותר כשהמשתנה הוא קלט משתמש.

Heredocs#

Heredocs הם דמויי־מרכאות־כפולות אלא אם הסמן הוא במרכאות יחידות:

my $msg = <<EOM;
Hello, $name!
The hash is %h.
EOM
# "Hello, Alice!\nThe hash is %h.\n" - $name interpolated, %h literal

my $msg = <<'EOM';
Hello, $name!
EOM
# "Hello, $name!\n" - single-quoted marker disables interpolation

Heredocs מוזחים (Perl 5.26+) מתעלמים מרווח מוביל עד לרצף הקצר ביותר בכל שורה:

my $msg = <<~EOM;
    Hello, $name!
    EOM
# "Hello, Alice!\n" - leading "    " stripped from every line

הצורה ~ מועדפת בקוד מודרני; הצורה הלא־מוזחת מכוערת בתוך לולאות וגופי מתודה.

דוגמה אמיתית: בניית HTML בבטחה#

my @items = ('Apples', 'Bananas', 'Cherries');

my $html = <<~HTML;
    <ul>
    @{[ map { "  <li>$_</li>" } @items ]}
    </ul>
    HTML

ה־map מחזיר רשימה של מחרוזות <li>...</li>; @{[ ... ]} משבץ אותן; ה־$" המשתמע הוא רווח יחיד, ונותן:

<ul>
  <li>Apples</li>   <li>Bananas</li>   <li>Cherries</li>
</ul>

כדי לקבל שורות חדשות בין הפריטים, הגדירו $" באופן מקומי:

my $html = do {
    local $" = "\n";
    <<~HTML;
        <ul>
        @{[ map { "  <li>$_</li>" } @items ]}
        </ul>
        HTML
};

או חברו במפורש:

my $li = join "\n", map { "  <li>$_</li>" } @items;
my $html = "<ul>\n$li\n</ul>\n";

ה־join המפורש בדרך כלל ברור יותר עבור תבניות לא־טריוויאליות. הצורה @{[ ... ]} מצדיקה את מקומה כשהתבנית היא בעיקר טקסט מילולי עם חישוב משובץ אחד או שניים.

מה המשתנה המיוחד $" שולט בו#

מפריד הרשימה בשיבוץ:

my @x = (1, 2, 3);
print "x = @x\n";                         # "x = 1 2 3" - default $" is " "

local $" = ", ";
print "x = @x\n";                         # "x = 1, 2, 3"

local $" = "\n  ";
print "items:\n  @x\n";                   # "items:\n  1\n  2\n  3"

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

ראו גם#

  • סקלרים, מערכים, האשים - הצורות שהשיבוץ מרחיב.

  • הפניות - צורת ה־deref @{ EXPR } ומה ה־\ ב־${\ ... } עושה.

  • $" - מפריד הרשימה עבור שיבוץ מערך.

  • $,, $\, $/ - משתני המפריד הקשורים עבור print וקלט.

  • sprintf, printf

    • the explicit format-string alternative when the template is mostly punctuation.

  • qq, q, qw - האופרטורים דמויי־הציטוט עם מתחמים מותאמים.