Perl6学习笔记.docx
《Perl6学习笔记.docx》由会员分享,可在线阅读,更多相关《Perl6学习笔记.docx(75页珍藏版)》请在冰点文库上搜索。
Perl6学习笔记
第二章基础
假设有一场乒乓球比赛,比赛结果以这种格式记录:
Player1Player2|3:
2
这意味着选手1与选手2的比分为3:
2, 你需要一个脚本算出每位选手赢了几场比赛并且胜了几局。
输入数据(存储在一个叫做scores的文件中)像下面这样:
1BethAnaCharlieDave
2AnaDave|3:
0
3CharlieBeth|3:
1
4AnaBeth|2:
3
5DaveCharlie|3:
0
6AnaCharlie|3:
1
7BethDave|0:
3
第一行是选手清单。
随后每一行记录着比赛结果。
这里使用Perl6给出一种解决方案:
1usev6;
2
3my$file=open'scores';
4my@names=$file.get.words; #get方法读入一行
5
6my%matches;#赢得比赛次数
7my%sets;#赢得比赛局数
8
9for$file.lines->$line{
10my($pairing,$result)=$line.split('|');#对剩下的每一行调用split操作
11my($p1,$p2)=$pairing.words;#提取选手1和选手2的名字
12my($r1,$r2)=$result.split(':
');#提取比赛比分
13
14%sets{$p1}+=$r1;#选手1赢得的比赛局数
15%sets{$p2}+=$r2;#选手2赢得的比赛局数
16
17if$r1>$r2{#如果每场比赛中,选手1赢的局数多于选手2,则选手1赢得的比赛数+1,反之选手2的+1
18%matches{$p1}++;
19}else{
20%matches{$p2}++;
21}
22}
23
24my@sorted=@names.sort({%sets{$_}}).sort({%matches{$_}}).reverse;
25
26for@sorted->$n{
27say"$nhaswon%matches{$n}matchesand%sets{$n}sets";
28}
输出如下:
Anahaswon2matchesand8sets
Davehaswon2matchesand6sets
Charliehaswon1matchesand4sets
Bethhaswon1matchesand4sets
每个Perl6程序应该以usev6;作为开始,它告诉编译器程序期望的是哪个版本的Perl。
在Perl6中,一个变量名以一个魔符打头,这个魔符是一个非字母数字符号,诸如$,@,%或者&,还有更少见的双冒号:
:
内置函数open打开了一个名叫scores的文件,并返回一个文件句柄,即一个代表该文件的对象。
赋值符号=将句柄赋值给左边的变量,这意味着$file现在存储着该文件句柄。
my@names=$file.get.words;
上边这句的右侧对存储在$file中的文件句柄调用了get方法,get方法从文件中读取并返回一行,并去掉行的末尾。
.words
也是一个方法,用于从get方法返回的字符串上。
.words方法将它的组件--它操作的字符串,分解成一组单词,这里即意味着不含空格的字符串。
它把单个字符串'BethAnaCharlieDave'转换成一组字符串'Beth','Ana','Charlie','Dave'.最后,这组字符串存储在数组@names中。
1my%matches;
2my%sets;
在比分计数程序中,%matches存储每位选手赢得的比赛数。
%sets存储每位选手赢得的比赛局数。
1for$file.lines->$line{
2...
3}
for循环中$file.lines产生一组从文件scores读取的行,从上次$file.lines离开的地方开始,一直到文件末尾结束。
在第一次循环中,$line会包含字符串AnaDave|3:
0;在第二次循环中,$line会包含CharlieBeth|3:
1,以此类推。
1my($pairing,$result)=$line.split('|');
split此处是一个方法,字符串'|'是它的参数。
第一次循环结束:
Variable Contents
$line 'AnaDave|3:
0'
$pairing 'AnaDave'
$result '3:
0'
$p1 'Ana'
$p2 'Dave'
$r1 '3'
$r2 '0'
1my@sorted=@names.sort({%sets{$_}}).sort({%matches{$_}}).reverse;
这一句是排序,先按比赛局数多少排序,再按赢得的比赛数排序,然后反转。
打印选手名字的时候以胜负次序排序,代码必须使用选手的分数,而非他们的名字来进行排序。
sort方法的参数是一个代码块,用于将数组元素(选手的名字)转换成用于排序的数据。
数组的元素通过变量$_传递到代码块中。
最简单的使用分数排序选手的方法应该是@names.sort({%matches{$_}}),这是通过使用赢得比赛的次数来进行排序。
然而,Ana和Dave都赢了两场比赛。
还需要比较谁赢的的比赛局数多,才能决定比赛的排名。
在双引号括起的字符串中,标量和花括号中的变量能进行变量插值。
1my$names='things';
2say'Donotcallme$names';#Donotcallme$names
3say"Donotcallme$names";#Donotcallmethings
花括号中的数组进行插值后会变成用空格分隔的条目。
花括号中的散列插值后每个散列键值对单独成为一行,每行包含一个健,随后是一个tab符号,然后是键值,最后是一个新行符。
TODO:
explain<...>quote-words
1say"Math:
{1+2}"#Math:
3
2my@people=;
3say"Thesynopticsare:
{@people}"#Thesynopticsare:
LukeMatthewMark
4
5say"{%sets}";#Fromthetabletennistournament
6
7#Charlie4
8#Dave6
9#Ana8
10#Beth4
Whenarrayandhashvariablesappeardirectlyinadouble-quotedstring(andnotinside
curlybrackets),theyareonlyinterpolatediftheirnameisfollowedbyapostcircumfix–
abracketingpairthatfollowsastatement.It’salsooktohaveamethodcallbetweenthe
variablenameandthepostcircumfix.
1my@flavours=;
2
3say"wehave@flavours";#wehave@flavours
4say"wehave@flavours[0]";#wehavevanilla
5#so-called"Zenslice"
6say"wehave@flavours[]";#wehavevanillapeach
7
8#methodcallsendinginpostcircumfix
9say"wehave@flavours.sort()";#wehavepeachvanilla
10
11#chainedmethodcalls:
12say"wehave@flavours.sort.join(',')";
13#wehavepeach,vanilla
练习:
例子中的第一行选手的名字是多余的,你可以在参加比赛的选手中找出所有选手的名字!
如果例子中的第一行被省略了,你如何更改程序?
提示:
%hash.keys返回散列%hash中的所有键。
答案:
移除此行:
my@names=$file.get.words;,并且将
my@sorted=@names.sort({%sets{$_}}).sort({%matches{$_}}).reverse;
变成:
my@sorted=%sets.keys.sort({%sets{$_}}).sort({%matches{$_}}).reverse;
2.除了移除冗余,你也可以用它来提醒我们,如果一个选手没有在第一行的名字清单中被提到,例如因为输入错误,你该怎样修改你的程序?
答案:
引入另外一个散列,合法选手的名字作为键,当读取选手名字的时候查找该散列:
1
2my@names=$file.get.split('');
3my%legitimate-players;
4for@names->$n{
5%legitimate-players{$n}=1;
6}
7
8...
9
10for$file.lines->$line{
11my($pairing,$result)=$line.split('|');
12my($p1,$p2)=$pairing.split('');
13for$p1,$p2->$p{
14if!
%legitimate-players{$p}{
15say"Warning:
'$p'isnotonourlist!
";
16}
17}
18
19...
20}
第三章操作符
usev6;
my@scores='Ana'=>8,'Dave'=>6,'Charlie'=>4,'Beth'=>4;
my$screen-width=30;
my$label-area-width=1+[max]@scores».key».chars;
my$max-score=[max]@scores».value;
my$unit=($screen-width-$label-area-width)/$max-score;
my$format='%-'~$label-area-width~"s%s\n";
for@scores{
printf$format,.key,'X'x($unit*.value);
}
在这个例子中,我们计算一下每位选手在竞标赛中赢得比赛的局数。
my@scores='Ana'=>8,'Dave'=>6,'Charlie'=>4,'Beth'=>4; 这一句局包含了三个不同的操作符=和=>和,
以字符串连接操作符~为例,$string~="text"等价于$string=$string~"text".
=>操作符(大键号)创建了一个键值对对象,一个键值对存储着键和值;键在=>操作符的左侧,值在右侧。
这个操作符有一个特殊的特性:
编译器会把=>操作符左侧的任何裸标识符解释为一个字符串。
你也可以这样写:
my@scores=Ana=>8,Dave=>6,Charlie=>4,Beth=>4;
最后逗号操作符,构建了一个对象序列,在该情况下,所谓的对象就是键值对。
这三个操作符都是中缀操作符,这意味着它在两个条目之间。
一个项前面可以有0个或多个前缀操作符,所以你可以写比如4+-5。
+号(一个中缀操作符)的后面,编译器期望一个项,为了将-号解释为项5的一个前缀。
my$label-area-width=1+[max]@scores».key».chars;
»是一个特殊的符号,打印不出来可以用两个大于号>>代替。
中缀操作符max返回两个值中的较大者,所以2max3返回3。
方括号包裹着一个中缀操作符让Perl将该中缀操作符应用到列表中的元素之间。
[max]1,5,3,7和1max5max3max7一样,结果都为7.
同样地,[+]用来计算列表元素的和,[*]用来计算列表元素的积,[<=]用来检查一个列表的值是否按递增排序。
@scores».key».chars
>my@scores=Ana=>8,Dave=>6,Charlie=>4,Beth=>4;
Ana 8Dave 6Charlie 4Beth 4
>@scores.key
Method'key'notfoundforinvocantofclass'Array'
>@scores>>.key
AnaDaveCharlieBeth
就像@variable.method在@variable上调用一个方法一样,@array».method对@array中的每一项调用method方法,并且返回一个返回值的列表。
即@scores>>.key返回一个列表。
>@scores>>.key>>.chars #每个名字含有几个字符
3474
表达式[max]@scores».key».chars给出(3,4,7,4)中的最大值。
它与下面的表达式相同:
1@scores[0].key.chars
2max@scores[1].key.chars
3max@scores[2].key.chars
4max...
>@scores[0]
"Ana"=>8
> @scores[0].key
Ana
1my$format='%-'~$label-area-width~"s%s\n";
2for@scores{
3printf$format,.key,'X'x($unit*.value);
4}
定义一个格式,%-表示左对齐,~是字符串连接操作符.for循环中,@scores中的每一项被绑定给特殊变量$_,.key是每项的键,即名字,.value是每项的键值,即得分。
小x是字符串重复操作符。
3.1关于优先级的的一句话
my@scores='Ana'=>8,'Dave'=>6,'Charlie'=>4,'Beth'=>4;
等号右侧产生一个列表(因为逗号,操作符),这个列表由对儿组成(因为=>),并且结果赋值给数组变量。
在Perl5中会这样解释:
1(my@scores='Ana')=>8,'Dave'=>6,'Charlie'=>4,'Beth'=>4;
以至于数组@scores中只有一个项,表达式的其余部分被计算后丢弃。
优先级规则控制着编译器如何解释这一行。
Perl6的优先级规则申明中缀操作符=>比,中缀操作符对于参数的绑定更紧,而逗号操作符比等号赋值操作符绑定的更紧。
实际上有两种不同优先级的赋值操作符。
当赋值操作符右侧是一个标量时,使用较紧优先级的项赋值操作符,否则使用较松优先级的列表赋值操作符。
(如同螺丝的松紧)
比较 $a=1,$b=2和@a=1,2,前者是在一个列表中赋值给两个变量,后者是将含有两个项的一个列表赋值给一个变量。
1say5-7/2;#5-3.5=1.5
2say(5-7)/2;#(-2)/2=-1
Perl6中的优先级可以用圆括号改变,但是如果圆括号直接跟在标识符的后面而不加空格的话,则会被解释为参数列表。
例如:
say(5-7)/2;#-2
只打印出了5-7的值。
优先级表
3.2比较和智能匹配
1my@a=1,2,3;
2my@b=1,2,3;
3
4say@a===@a;#Bool:
:
True
5say@a===@b;#Bool:
:
False
6
7#theseuseidentityforvalue
8
9say3===3#Bool:
:
True
10say'a'==='a';#Bool:
:
True
11
12my$a='a';
13say$a==='a';#Bool:
:
True
>@b===@a
False
>@aeqv@b
True
>'2'eqv2
False
只有当两个对象有相同的类型和相同的结构时,eqv操作符才返回True。
在前面定义的例子中,@a eqv @b结果为True,因为@a和@b各自包含相同的值,另一方面,'2'eqv2返回'False',因为一个参数是字符串,另一个是整数,类型不相同。
3.2.1数字比较
使用==中缀操作符查看两个对象是否有相同的数字值。
如果某个对象不是数字,Perl会在比较之前尽力使其数字化。
如果没有更好的方式将对象转换为数字,Perl会使用默认的数字0。
1say1==1.0;#Bool:
:
True
2say1=='1';#Bool:
:
True
3say1=='2';#Bool:
:
False
4say3=='3b';#fails
跟数字比较相关的还有<,<=,>,>=。
如果两个对象的数字值不同,使用!
=会返回True。
如果你将数组或列表作为数字,它会计算列表中项的个数。
1my@colors=;
2
3if@colors==3{
4say"It'strue,@colorscontains3items";
5}
3.2.2 字符串比较
Perl6中使用eq比较字符串,必要时会将其参数转换为字符串。
1if$greetingeq'hello'{
2say'welcome';
3}
Table3.2:
OperatorsandComparisons
数字比较
字符串比较
意思
==
eq
等于
!
=
ne
不等于
!
==
!
eq
不等于
<
lt
小于
<=
le
小于或等于
>
gt
大于
>=
ge
大于或等于
例如,'a'lt'b'为true,'a'lt'aa'也为true。
!
=是!
==的便捷形式,它实际是!
元操作符加在中缀操作符==之前。
同样地,ne和!
eq是一样的。
三路操作符
三路操作符有两个操作数,如果左侧较小,返回Order:
:
Increase,两侧相等则返回Order:
:
Same,如果右侧较小则返回Order:
:
Decrease。
对于数字使用三路操作符<=>,对于字符串,使用三路操作符leg(取自lesser,equal,greater)。
中缀操作符cmp是一个对类型敏感的三路操作符,它像<=>一样比较数字,像leg一样比较字符串,(举例来说)并且比较键值对儿时,先比较键,如果键相同再比较键值:
1say10<=>5;#+1
2say10leg5;#because'1'lt'5'
3say'ab'leg'a';#+1,lexicographiccomparison
三路操作符的典型用处就是用在排序中。
列表中的.sort方法能使用一个含有两个值的块或一个函数,比较它们,并返回一个小于,等于或大于0的值。
sort 方法根据该返回值进行排序:
1say~.sort;
2#output:
Concreteabstract
3
4say~.sort:
->$a,$b{uc($a)leguc($b)};
5 #output:
abstractConcrete
默认的,比较是大小写敏感的,通过比较它们的大写变形,而不是比较它们的值,这个例子使用了大小写敏感排序。
3.2.3智能匹配
使用~~做正确的事情。
1if$pints-drunk~~8{
2say"Gohome,you'vehadenough!
";
3}
4
5if$country~~'Sweden'{
6say"Meatballswithlingonb