Everyday Perl 6

分类: 计算机技术 | 由 zhang发布于

Perl 6 很快就会发布了. 对于普通的Perl程序员来说,在Perl 6中编程和在Perl 5中有什么不同呢? 答案是:很不同但也很类似。一个用Perl 6写的程序乍一看和Perl 5的差不多。用Perl 6编程仍然有用Perl编程的感觉。然而,所不同的是,Perl 6通过给程序员更多的工具使得他们具有更强的表达力,并使其表达更为精确。

Perl 6中的很多改变使得编程新手或者从其他语言过来的人能更容易地理解这门语言,与此同时,这些变化并不只是简单地自身的改变。如果你最喜欢的Perl 5的语法部分是用箭头来调用对象的方法,千万不要对Perl 6用点来表示感到沮丧。 设计者仔细地考虑了每个语法的变化,来确保Perl 6依然具有Perlish的本质,并确保每个改变都是全面的改进。有些Perl程序员喜欢这个语言与众不同的语法,但当与Perl文化的大背景相比(包括语言本身、CPAN和程序员社区),有些不同并不是那么重要。

魔法符号的不变性

一个基本的改变就是,当你表示一个集合(数组或hash)中的单个元素时,魔法符号不变,而不必改变前面的魔法符号来指示你获得的数据的类型。

例如,在Perl 5和Perl 6中你都可以这样创建并初始化一个集合变量:

my @array = (1,3,5,12,37,42);
my %hash = ( alpha => 4, beta => 6 );

你如何存取那些集合中的单个元素就看起来有些不同:

# Perl 6          # Perl 5
my $third = @array[2];   my $third = $array[2];
my $beta = %hash{'beta'};  my $beta = $hash{'beta'};

长期使用 Perl 5 的程序员会疑惑 Perl 6 中的切片是如何工作的。答案是,和 Perl 5 中的一样。

my @odds = @array[1,3,5]; # 数组切片
my @bets = %hash{'alpha','beta'}; # hash 切片

唯一不同的是,Perl 5中的hash切片是以@魔法符号开头的。

新的括号

这些hash例子中,引用hash的索引是比较笨拙的。Perl 5有个语法捷径允许$hash{word}$hash{'word'}一样工作。但是这样使用的问题是,当你的word是一个子过程的名字的话,而且你想要Perl执行这个子过程时,就会引起混淆。

在Perl 6中,存取hash元素有一个语法捷径,是利用了“引用字”操作符的名字的变化:
# Perl 6   # Perl 5
my @array = < foo bar baz >; my @array = qw(foo bar baz);
my %hash = < a b c d e f g h >; my %hash = qw(a b c d e f g h);
my $queue = %hash; my $queue = $hash{'q'};
my @vows = %hash; my @vows = @hash{qw(c a g e)};

另外,就像双引号字符串会执行变量内插而单引号不会,双括号引用也会有变量内插:

my $foo = "This is";
my $bar = "the end";
my @blah = << $foo $bar >>; # ('This','is','the','end');

注意,内插发生在引用字作为操作符起作用之前。

说到内插,双引号字符串中的内插有一点变化。如将一个数组插入到字符串中,你必须在数组后名带一对空的方括号。这样当你的字符串中含有email地址时,就消除了是否要进行变量内插的歧义。

my @items = < names addresses email >;
say "Send @items[] to test@foo.com";
# Send names addresses email to test@foo.com

你也可以在双引号字符串中内插更多的东西:

say "Send me $person.name()"; # results of a method call
say "2 + 2 = { 2+2 }"; # any bit of perl code

第二个例子意味着你在双引号字符串中插入花括号时要小心了,但这对于能够插入任意Perl的代码结果的好处来说,这个代价还是比较小的。

顺便,要习惯使用say子过程。它和print是一样的,只是结尾多了一个换行而已。很有用。

更少的括号

在Perl 5中经常用到括号的地方,现在Perl6 中已经不再用了:

# Perl 6 # Perl 5
if $cond { ... } if ($cond) { ... }
unless $cond { ... } unless ($cond) { ... }
while $cond { ... } while ($cond) { ... }
for @array { ... } for (@array) { ... }

在Perl 6 中,只是在分组的时候括号才是必需的。

语法

另外一个大的变化是,Perl 5 中的一些标准语法在Perl 6 中看上去不一样了。特别是,从文件中读入行的标准语法使用了for循环而不是while循环:

# Perl 6 # Perl 5
for =$fh { ... } while (<$fh>) { ... }
for =<> { ... } while (<>) { ... }

Perl 5 程序员很可能在想,“但是,将文件句柄的读取放在列表上下文中,会不会导致将整个文件读入内存中?”答案是“是”也“不是”。是的,它是在列表上下文中,但是在Perl 6 中,所有的列表都是惰性的,所以如没有必要它是不会读取内容的。

在这个例子中,一元操作符=是引起iterator进行遍历的操作符。如果$fh 是一个文件句柄, =$fh 将会每次读取一行而遍历整个文件。在标量环境下=$fh 会读取一行,在列表上下文中它将会每次读取一行直到文件的末尾。遍历一个空的字符串(记住尖括号的新用途),就等同于Perl 5 中的从命令行读取文件。

操作符重命名

很多常用的操作符在Perl 6 中有了新的符号。这种符号的变化使得整个语言更加有规则,这样就使得解析变得更容易,但更重要的是便于人们记忆:

# Perl 6   # Perl 5
$object.method(@args); $obj->method(@args);
$x = $cond ?? $true !! $false; $x = $cond ? $true : $false;
$s = "con" ~ "cat" ~ "enate"; $s = "con" . "cat" . "enate";
$str ~~ /$pattern/; $str =~ /$pattern/;

在任何时候你看到Perl 6 中的~ , 它一定是处理字符串的某种东西。it has something to do with strings. 一元的 ~ 将其右值放置于字符串上下文中,二元的 ~ 是一个字符串连接器,两个连写的 ~ 能让你用一个正则表达式匹配一个字符串。 (事实上,它可以干更多的事情。但是作为一个前瞻性东西,我们没必要彻底搞清楚。另外从Perl 5 的了解来看,现在知道这么多也够了~就是匹配字符串。).

新 Perl 6 语法

Perl 6 也有一些新的语法
长注释

很多人抱怨 Perl 5 缺少容易的多行注释机制(POD 显然太?唆了)。 Perl 6 很好地解决了这个问题。如果注释字符#后面紧跟着一个方括号,那么注释将会延续到下一个匹配的关闭括号。

#[ This is a
multi-line comment
that ends here ----> ]

参考Whitespace and Comments in Synopsis 02 更多信息。

分支 声明

对那些永远希望Perl中有分支声明的人们,Perl 6 也准备了,只不过是另外一个名字。

given $thing {
when 3 { say "three"; }
when 5 { say "five"; }
when 9 { say "nine"; }
when "a" { say "what?"; }
default { say "none"; }
}

这个结构比我在这里所概述的功能强大的多。当$thing 是一个对象、数组或者Hash甚至代码时,此结构都能利用智能匹配操作符来作出正确的反应。

新的循环

C风格的循环操作符 for 现在变成了 loop. 但你可以省略括号里的部分写出一个无限循环:

loop { ... }

另一个新的循环结构是 repeat 循环,这替代了 Perl 5里面的 do-while. 与 do-while最大的不同是, repeat是一个真正的循环,因此你可以随意使用 next, last, redo 它会工作的很好。

更多信息请参考 Synopsis 04.

参数化的块

从本质上讲,Perl 6 中的所有的区块都是一个子过程。有的区块是没有参数的,如 if 声明中所用的;但是其他的是有参数的,比如 for 的循环体。 不过,任何区块都是可以参数化的。这在做一些 Perl 5 不容易办到的事情时特别有用??如利用 map 一次检查三个值:


my @trimults = map -> $a,$b,$c { $a * $b * $c }, @numbers;

这个例子中 Perl 6 给箭头指派了一个更高级的用途??将参数传入一个block。 你很可能会在 for 循环中看到这样的代码:

# Perl 6        # Perl 5
for @array -> $a { ... }   for my $a (@array) { ... }
for @array -> $a, $b { ... }   # too complex :)

第二个 for 循环将每次从 @array 取两个项目并将之分别分配给 $a$b 以便在block中使用。在 Perl 5 中很难完成类似的行为。

@trimults 例子的另一种方式,更简洁一些:

my @trimults = map { $^a * $^b * $^c }, @numbers;

魔法符号后紧跟一个^ 的变量是传入block的隐含变量,Perl 6 以Unicode的顺序来分配它们。也就是说, $^a 是第一个参数, $^b 是第二个, $^c是第三个。

还有写参数化block的第三种方式,会比较复杂但是更强大。它使得程序员能充分利用子过程签名的好处。是的, TMTOWTDI 依然有效且很好用。 :-)

子过程(函数)签名

你仍然可以用Perl 5中的方式来写子过程,但是Perl 6可以让你指定一个签名,来描述有多少个参数传递给子过程、哪些参数是可选的、哪些是positional的、哪些是命名的、未传递的参数的默认值是多少、哪些参数复制了传递的值、哪些参数是变量的别名,等等。

关于Perl 6子程序更多的信息, 请参阅 Synopsis 06 和 Phil Crow 最近的文章 The Beauty of Perl 6 Parameter Passing.

变量类型

为了让程序员的表达更精确, Perl 6 允许可选的变量类型。也就是说,程序员不但能说“这个变量是个标量”,而且也可以说,“在这个特定的类中,这个标量符合对象的期望”。换句话说,你可以这么写:

my Dog $spot;
my Fish $wanda;

...这对Perl和程序员都是有实际含义的。变量 $spot 只有在 Perl 期望 Dog 的时候才可用,并且变量 $wanda 只在 Perl 期望 Fish 的时候可用。然而, Perl 5 风格的代码也可以完美地工作:

my Dog $spot;
my Fish $wanda;
my $x;
$x = $spot;
$x = $wanda;

...因为 $x 是一个为定义类型的变量,它可以接受 DogFish,或者其他任何标量。

多重调用

变量类型结合子过程签名就可以得到多调用的好处。这意味着你可以声明两个名字相同的子过程,但是它们的签名不同,Perl 会根据传递给它的参数的类型在运行时决定调用哪个子过程。例如:

multi sub feed(Dog $spot)
{ say "dog food!"; }
multi sub feed(Fish $wanda)
{ say "fish food!"; }
my Fish $nemo;
my Dog $rover;
feed($nemo); # fish food!
feed($rover); # dog food!

关键字 multi 会告诉Perl你想声明同名的多个子过程,Perl应该利用名字和参数和其他可以区分的特征来决定该调用哪一个。

结束

我希望这个简介能给你一个对Perl 6 的变化的感觉,并说明这些改变是有益并且有用的。

Perl 6 原型的实现(叫 pugs )应该能够执行本文所给出的所有例子。如果不能执行,请登录IRC节点,进入 #perl6, 提交一个测试,会有pugs的开发人员更新pugs的。 :-)

致谢

特别感谢 IRC (#perl and #perl6) 上的人们,他们查看了这篇文章,并给出了他们的输入和注释。

By Jonathan Scott Duff
May 10, 2007

Perl.com Compilation Copyright ? 1998-2006 O'Reilly Media, Inc.


我的相关文章:

最近阅读过此文章的网友:
Trackback url : u can trackback from your own site

1条评论 :

  1. ZhuZhuZhuZhu 评论道:

    不错,谢谢翻译

发表看法