Q 语言§

字符串通常在 Raku 代码中使用某种形式的引用结构来表示。其中最简化的形式是 Q,可以通过快捷方式 「…」 使用,或者通过 Q 后跟任何一对包围文本的定界符来使用,包括许多 Unicode 对。但是,大多数情况下,您只需要 '…'"…",将在以下部分中详细介绍。

有关在正则表达式中应用引用的信息,请参阅 正则表达式文档

字面字符串:Q§

Q[A literal string]
More plainly.
Q^Almost any non-word character can be a delimiter!^
QDelimiters can be repeated/nested if they are adjacent.
QQuoting with fancy unicode pairs

定界符可以嵌套,但在普通的 Q 形式中,不允许反斜杠转义。换句话说,基本的 Q 字符串尽可能地字面化。

某些定界符不允许紧跟在 Qqqq 之后。任何在 标识符 中允许的字符都不允许使用,因为在这种情况下,引用结构连同这些字符一起被解释为标识符。此外,( ) 不允许,因为这被解释为函数调用。如果您仍然希望使用这些字符作为定界符,请用空格将它们与 Qqqq 分隔开。请注意,某些自然语言在字符串的右侧使用左定界引号。Q 不支持这些,因为它依赖于 unicode 属性来区分左右定界符。

Q'this will not work!'
Q(this won't work either!)

上面的示例将产生错误。但是,这将起作用

Q (this is fine, because of space after Q)
Q 'and so is this'
Q<Make sure you <match> opening and closing delimiters>
Q{This is still a closing curly brace → \}

这些示例产生

this is finebecause of space after Q
and so is this
Make sure you <match> opening and closing delimiters
This is still a closing curly brace → \

引用结构的行为可以通过副词来修改,如后面的部分中详细解释。

含义
:x:exec作为命令执行并返回结果
:w:words按单词拆分结果(无引号保护)
:ww:quotewords按单词拆分结果(有引号保护)
:q:single插值 \\、\qq[...] 和使用 \ 转义定界符
:qq:double使用 :s、:a、:h、:f、:c、:b 插值
:s:scalar插值 $ 变量
:a:array插值 @ 变量(在后缀之后)
:h:hash插值 % 变量(在后缀之后)
:f:function插值 & 调用
:c:closure插值 {...} 表达式
:b:backslash启用反斜杠转义(\n、\qq、\$foo 等)
:to:heredoc将结果解析为 heredoc 终止符
:v:val如果可能,转换为同形异义词

这些副词可以与 Q 一起使用,因此即使引用运算符不插值,它也会插值

my %þ = :is-mighty;
say Q "Þor %þ<>";                         # OUTPUT: «Þor %þ<>␤» 
say Q:h"Þor %þ<>";                        # OUTPUT: «Þor is-mighty   True␤» 
%þ = :42foo, :33bar;
say Q:h:c "Þor %þ<> →  { [+] %þ.values}"# OUTPUT: «Þor bar 33␤foo  42 →  75␤» 
my @þ= <33 44>say Q:a "Array contains @þ[]"# OUTPUT: «Array contains 33 44␤» 
say Q:v<33> + 3;                          # OUTPUT: «36␤» 

默认情况下,如所示,Q 直接引用,不会对引用的字符串进行任何形式的转换。副词将修改其行为,例如,将字符串转换为同形异义词(使用 :v 副词),或允许插值哈希(通过 :h)或 {} 代码段(通过 :c)。数组和哈希必须后跟后缀;也就是说,带符号的标识符不会插值,但后跟索引、decont 运算符或带括号的方法调用,它将

my @þ= <33 44>;
say Q:a "Array contains @þ.elems()"# OUTPUT: «Array contains 2␤» 

在没有括号的情况下,相同的代码在没有后缀运算符的情况下,将不会插值。

转义:q§

'Very plain';
q[This back\slash stays];
q[This back\\slash stays]# Identical output 
q{This is not a closing curly brace → \}, but this is → };
Q :q $There are no backslashes here, only lots of \$\$\$!$;
'(Just kidding. There\'s no money in that string)';
'No $interpolation {here}!';
Q:q!Just a literal "\n" here!;

q 形式允许使用反斜杠转义否则会结束字符串的字符。反斜杠本身也可以转义,如上面的第三个示例所示。通常的形式是 '…'q 后跟一个定界符,但它也可以作为 Q 上的副词使用,如上面的第五个和最后一个示例所示。

这些示例产生

Very plain
This back\slash stays
This back\slash stays
This is not a closing curly brace → } but this is →
There are no backslashes here, only lots of $$$!
(Just kidding. There's no money in that string)
No $interpolation {here}!
Just a literal "\n" here

\qq[...] 转义序列启用 qq 插值 用于字符串的一部分。当您的字符串中包含 HTML 标记时,使用此转义序列非常方便,以避免将尖括号解释为哈希键

my $var = 'foo';
say '<code>$var</code> is <var>\qq[$var.uc()]</var>';
# OUTPUT: «<code>$var</code> is <var>FOO</var>␤»

插值:qq§

my $color = 'blue';
say "My favorite color is $color!"# OUTPUT: «My favorite color is blue!␤» 

qq 形式 - 通常使用双引号编写 - 允许插值反斜杠转义序列(如 q:backslash)、所有带符号变量(如 q:scalar:array:hash:function)以及 {...} 内的任何代码(如 q:closure)。

插值变量§

qq 引用的字符串中,您可以使用带符号的变量来触发变量值的插值。带有 $ 符号的变量会在它们出现时进行插值(除非转义);这就是为什么在上面的示例中,"$color" 变成了 blue

但是,带有其他符号的变量只有在您在变量后面加上相应的后缀([] 用于数组,<> 用于哈希,& 用于子程序)时才会触发插值。这允许您编写像 "[email protected]" 这样的表达式,而不会插值 @raku 变量。

要插值一个数组(或其他 Positional 变量),请在变量名后面附加一个 []

my @neighbors = "Felix""Danielle""Lucinda";
say "@neighbors[] and I try our best to coexist peacefully."
# OUTPUT: «Felix Danielle Lucinda and I try our best to coexist peacefully.␤» 

或者,除了使用 [] 之外,您还可以通过在方法名后面加上括号来调用方法,从而插值数组。因此,以下代码将起作用

say "@neighbors.join('') and I try our best to coexist peacefully."
# OUTPUT: «Felix, Danielle, Lucinda and I try our best to coexist peacefully.␤» 

但是,"@example.com" 会生成 @example.com

要调用子程序,请使用 & 符号,并在子程序名后面加上括号。

say "uc  'word'";  # OUTPUT: «uc  'word'»␤ 
say "&uc 'word'";  # OUTPUT: «&uc 'word'»␤ 
say "&uc('word')"# OUTPUT: «WORD»␤ 
# OUTPUT: «abcDEFghi␤»

要插值一个哈希(或其他 Associative 变量),请使用 <> 后缀运算符。

my %h = :1st; say "abc%h<st>ghi";
# OUTPUT: «abc1ghi␤»

qq 插值变量的方式与 q:scalar:array:hash:function 相同。您可以使用这些副词(或它们的简写形式 q:s:a:h:f)来插值变量,而不会启用其他 qq 插值。

插值闭包§

qq 的另一个特性是能够使用花括号从字符串中插值 Raku 代码

my ($x$y$z= 43.53;
say "This room is {$x}m by {$y}m by {$z}m.";               # OUTPUT: «This room is 4m by 3.5m by 3m.␤» 
say "Therefore its volume should be { $x * $y * $z }m³!";  # OUTPUT: «Therefore its volume should be 42m³!␤» 

这提供了与 q:closure/q:c 引号形式相同的功能。

插值转义代码§

qq 引号形式还插值反斜杠转义序列。其中一些打印不可见/空白 ASCII 控制代码或空白字符

序列十六进制值字符参考 URL
\0\x0000Nulhttps://util.unicode.org/UnicodeJsps/character.jsp?a=0000
\a\x0007Belhttps://util.unicode.org/UnicodeJsps/character.jsp?a=0007
\b\x0008退格https://util.unicode.org/UnicodeJsps/character.jsp?a=0008
\e\x001BEschttps://util.unicode.org/UnicodeJsps/character.jsp?a=001B
\f\x000C换页https://util.unicode.org/UnicodeJsps/character.jsp?a=000C
\n\x000A换行https://util.unicode.org/UnicodeJsps/character.jsp?a=000A
\r\x000D回车https://util.unicode.org/UnicodeJsps/character.jsp?a=000D
\t\x0009制表符https://util.unicode.org/UnicodeJsps/character.jsp?a=0009

qq 还支持两个多字符转义序列:\x\c。您可以使用 \x\x[] 以及 Unicode 字符的十六进制代码或字符列表

my $s = "I \x2665 Raku!";
say $s;
# OUTPUT: «I ♥ Raku!␤» 
 
$s = "I really \x[2661,2665,2764,1f495] Raku!";
say $s;
# OUTPUT: «I really ♡♥❤💕 Raku!␤»

您还可以使用 \c 和该字符的 unicode 名称命名序列名称别名 来创建 Unicode 字符

my $s = "Camelia \c[BROKEN HEART] my \c[HEAVY BLACK HEART]!";
say $s;
# OUTPUT: «Camelia 💔 my ❤!␤»

有关更多详细信息,请参阅 Unicode 文档页面上对 \c[] 的描述。

qq 提供与 q:backslash/q:b 提供的相同的转义序列插值。

阻止插值和处理缺失值§

您可以通过转义符号或其他初始字符来阻止 qq 引用的字符串中的任何不必要的插值

say "The \$color variable contains the value '$color'"# OUTPUT: «The $color variable contains the value 'blue'␤» 

对未定义值的插值将引发一个控制异常,该异常可以在当前块中使用 CONTROL 捕获。

sub niler {Nil};
my Str $a = niler;
say("$a.html""sometext");
say "alive"# this line is dead code 
CONTROL { .die };

单词引用:qw §

qw|! @ # $ % ^ & * \| < > | eqv '! @ # $ % ^ & * | < >'.words.list; 
q:w { [ ] \{ \} }           eqv ('['']''{''}');
Q:w | [ ] { } |             eqv ('['']''{''}');

:w 形式,通常写成 qw,将字符串拆分为“单词”。在此上下文中,单词被定义为由非空白字符组成的序列,这些序列由空白字符隔开。q:wqw 形式继承了 q 和单引号字符串分隔符的插值和转义语义,而 QwQ:w 继承了 Q 引号的非转义语义。

此形式优先于使用多个引号和逗号来表示字符串列表。例如,您可以这样写

my @directions = 'left''right,''up''down';

写和读这个更容易

my @directions = qw|left right up down|;

单词引用: < > §

say <a b c> eqv ('a''b''c');   # OUTPUT: «True␤» 
say <a b 42> eqv ('a''b''42'); # OUTPUT: «False␤», the 42 became an IntStr allomorph 
say < 42 > ~~ Int# OUTPUT: «True␤» 
say < 42 > ~~ Str# OUTPUT: «True␤» 

尖括号引用类似于 qw,但它具有额外的功能,可以让你构建 异形词 或某些数字的字面量

say <42 4/2 1e6 1+1i abc>.raku;
# OUTPUT: «(IntStr.new(42, "42"), RatStr.new(2.0, "4/2"), NumStr.new(1000000e0, "1e6"), ComplexStr.new(<1+1i>, "1+1i"), "abc")␤»

要构建 RatComplex 字面量,请在数字周围使用尖括号,不要添加任何额外的空格

say <42/10>.^name;   # OUTPUT: «Rat␤» 
say <1+42i>.^name;   # OUTPUT: «Complex␤» 
say < 42/10 >.^name# OUTPUT: «RatStr␤» 
say < 1+42i >.^name# OUTPUT: «ComplexStr␤»

42/101+42i 相比,没有涉及除法(或加法)运算。这对于例程签名中的字面量很有用,例如

sub close-enough-π (<355/113>{
    say "Your π is close enough!"
}
close-enough-π 710/226# OUTPUT: «Your π is close enough!␤» 
# WRONG: can't do this, since it's a division operation 
sub compilation-failure (355/113{}

带引号保护的单词引用: qww§

qw 形式的单词引用将按字面意义处理引号字符,并将它们保留在生成的单词中

say qw{"a b" c}.raku# OUTPUT: «("\"a", "b\"", "c")␤»

使用 qww 变体,你可以使用引号字符将字符串嵌入到单词引用结构中

say qww{"a b" c}.raku# OUTPUT: «("a b", "c")␤»

其他类型的引号也支持,它们具有通常的语义

my $one = 'here';
my $other = 'there';
say qww{ ’this and that’ “$one or $other” 「infinity and beyond」 }.raku;
# OUTPUT: «("this and that", "here or there", "infinity and beyond")␤»

嵌入字符串的分隔符始终被视为单词分隔符

say qww{'alpha'beta'gamma' 'delta'"epsilon"}.raku# OUTPUT: «("alpha", "beta", "gamma", "delta", "epsilon")␤»

带插值的单词引用: qqw§

qw 形式的单词引用不会插值变量

my $a = 42say qw{$a b c};  # OUTPUT: «$a b c␤»

因此,如果你希望在引用的字符串中插值变量,你需要使用 qqw 变体

my $a = 42;
my @list = qqw{$a b c};
say @list;                # OUTPUT: «[42 b c]␤»

请注意,变量插值发生在单词拆分之前

my $a = "a b";
my @list = qqw{$a c};
.say for @list# OUTPUT: «a␤b␤c␤»

带插值和引号保护的单词引用: qqww§

qqw 形式的单词引用将按字面意义处理引号字符,并将它们保留在生成的单词中

my $a = 42say qqw{"$a b" c}.raku;  # OUTPUT: «("\"42", "b\"", "c")␤»

使用 qqww 变体,你可以使用引号字符将字符串嵌入到单词引用结构中

my $a = 42say qqww{"$a b" c}.raku# OUTPUT: «("42 b", "c")␤»

嵌入字符串的分隔符始终被视为单词分隔符

say qqww{'alpha'beta'gamma' 'delta'"epsilon"}.raku# OUTPUT: «("alpha", "beta", "gamma", "delta", "epsilon")␤»

qqw 形式不同,插值也总是拆分(除了在嵌入字符串中发生的插值)

my $time = "now";
$_ = 'ni';
my @list = qqww<$time$time {6*7}{7*6} "$_$_">;
.say for @list# OUTPUT: «now␤now␤42␤42␤nini␤»

引号保护发生在插值之前,插值发生在单词拆分之前,因此来自插值变量内部的引号只是字面引号字符

my $a = "1 2";
say qqww{"$a$a}.raku# OUTPUT: «("1 2", "1", "2")␤» 
my $b = "\"2 3\"";
say qqww{"$b$b}.raku# OUTPUT: «("1 \"2 3\"", "1", "\"2", "3\"")␤»

带插值和引号保护的单词引用: « »§

这种引用风格类似于 qqww,但它还具有构建 异形词 的额外好处(使其在功能上等同于 qq:ww:v)。« » 的 ASCII 等效项是双尖括号 << >>

# Allomorph Construction 
my $a = 42say «  $a b c    ».raku;  # OUTPUT: «(IntStr.new(42, "42"), "b", "c")␤» 
my $a = 42say << $a b c   >>.raku;  # OUTPUT: «(IntStr.new(42, "42"), "b", "c")␤» 
 
# Quote Protection 
my $a = 42say «  "$a b" c  ».raku;  # OUTPUT: «("42 b", "c")␤» 
my $a = 42say << "$a b" c >>.raku;  # OUTPUT: «("42 b", "c")␤»

Shell 引用: qx§

要将字符串作为外部程序运行,不仅可以将字符串传递给 shellrun 函数,还可以执行 shell 引用。但是,有一些细微之处需要考虑。qx 引号不会插值变量。因此

my $world = "there";
say qx{echo "hello $world"}

只打印 hello。但是,如果你在调用 raku 之前声明了一个环境变量,它将在 qx 中可用,例如

WORLD="there" raku
> say qx{echo "hello $WORLD"}

现在将打印 hello there

调用 qx 的结果将被返回,因此此信息可以分配给一个变量以供以后使用

my $output = qx{echo "hello!"};
say $output;    # OUTPUT: «hello!␤»

另请参阅 shellrunProc::Async,了解执行外部命令的其他方法。

带插值的 Shell 引用: qqx§

如果希望在外部命令中使用 Raku 变量的内容,则应使用 qqx shell 引用结构

my $world = "there";
say qqx{echo "hello $world"};  # OUTPUT: «hello there␤»

再次,外部命令的输出可以保存在一个变量中。

my $word = "cool";
my $option = "-i";
my $file = "/usr/share/dict/words";
my $output = qqx{grep $option $word $file};
# runs the command: grep -i cool /usr/share/dict/words 
say $output;      # OUTPUT: «Cooley␤Cooley's␤Coolidge␤Coolidge's␤cool␤...»

注意在外部命令中使用的 Raku 变量的内容;恶意内容可用于执行任意代码。参见 qqx 陷阱

另请参见 runProc::Async,了解执行外部命令的更好方法。

Heredocs: :to§

使用heredoc编写多行字符串文字的一种便捷方法,它允许您自己选择分隔符。

say q:to/END/; 
Here is
some multi-line
string
END

heredoc的内容始终从下一行开始,因此您可以(也应该)完成该行。

my $escaped = my-escaping-function(q:to/TERMINATOR/language => 'html'); 
Here are the contents of the heredoc.
Potentially multiple lines.
TERMINATOR

如果终止符缩进,则从字符串文字中删除该缩进量。因此,此heredoc

say q:to/END/; 
    Here is
    some multi line
        string
    END

产生此输出

Here is
some multi line
    string

Heredocs 包括终止符之前的换行符。

要允许变量插值,请使用qq 形式,但您随后必须转义元字符\{ 以及$(如果它不是定义变量的符号)。例如

my $f = 'db.7.3.8';
my $s = qq:to/END/; 
option \{
    file "$f";
};
END
say $s;

将产生

option {
    file "db.7.3.8";
};

其他一些需要注意的情况是看起来无害的情况,其中文本看起来像 Raku 表达式。例如,以下内容会生成错误

my $title = 'USAFA Class of 1965';
say qq:to/HERE/;
<a href='https://usafa-1965.org'>$title</a>
HERE
# Output:
Type Str does not support associative indexing.
  in block <unit> at here.raku line 2

$title 右边的尖括号使它看起来像 Raku 的哈希索引,而实际上它是一个 Str 变量,因此出现了错误消息。一种解决方案是用花括号括起标量,这是在任何插值引号结构中输入表达式的其中一种方法。

say qq:to/HERE/;
<a href='https://usafa-1965.org'>{$title}</a>
HERE

另一种选择是转义< 字符以避免将其解析为索引运算符的开头。

say qq:to/HERE/;
<a href='https://usafa-1965.org'>$title\</a>
HERE

由于heredoc 可能非常长,但仍被 Raku 解释为单行,因此有时很难找到错误的来源。调试错误的一种粗略方法是从代码中第一个可见行开始,将其视为仅包含该行的heredoc。然后,直到出现错误,依次添加每一行。(创建 Raku 程序来执行此操作留给读者作为练习。)

您可以在同一行中开始多个 Heredocs。如果您这样做,第二个 heredoc 将在第一个 heredoc 结束之后才开始。

my ($first$second= qq:to/END1/qq:to/END2/; 
  FIRST
  MULTILINE
  STRING
  END1
   SECOND
   MULTILINE
   STRING
   END2 
say $first;  # OUTPUT: «FIRST␤MULTILINE␤STRING␤»
say $second; # OUTPUT: «SECOND␤MULTILINE␤STRING␤»

Unquoting§

文字字符串允许通过使用以下转义序列来插值嵌入的引号结构

my $animal="quaggas";
say 'These animals look like \qq[$animal]'# OUTPUT: «These animals look like quaggas␤» 
say 'These animals are \qqw[$animal or zebras]'# OUTPUT: «These animals are quaggas or zebras␤»

在此示例中,\qq 将执行双引号插值,而\qqw 将执行带插值的词语引号。如上所述转义任何其他引号结构将以相同的方式起作用,允许在文字字符串中进行插值。