Perl 6 圣誕月歷 (2009)

2009


有用的和有意思的循環(huán)


讓我們來看一個(gè)基本的例子.

for 1, 2, 3, 4 { .say }

這是一個(gè)最簡單清晰的語法的例子.在這并沒有使用括號(hào)來包起整個(gè)列表的語句,象這種寫法可以貫穿整個(gè) Perl 6. 通常比起 Perl 5 來你沒有必要寫那么多的括號(hào)了.

很象 Perl 5 , 這個(gè)循環(huán)中的值會(huì)默認(rèn)存到 $_ .在這個(gè)方法調(diào)用的 say 其實(shí)就是 $_.say.注意在 Perl 6 中,你不能直接只打一個(gè) say 而不加參數(shù),它會(huì)默認(rèn)使用 $_ 來傳參.你需要使用 .say 坤次。要么明確的指定是 $_.

下面這個(gè)語法塊并不是一個(gè)普通的塊.它能通過一個(gè)尖的指向,來告訴你的 循環(huán)變量傳進(jìn)去的參數(shù)的名字 .

for 1, 2, 3, 4 -> $i { $i .say }
如果你調(diào)用的 return 內(nèi)部有這個(gè),將返回閉合的子函數(shù).
這個(gè)尖尖也能取 多個(gè) 參數(shù).象下面這樣.

1
2
3

for 1, 2, 3, 4 -> $i , $j { "$i, $j" .say }
# 1 2
# 3 4

實(shí)際做了些什么啦楞卡?就是你在列表進(jìn)行迭代時(shí)一次取了兩個(gè)元素 . 如果你不明確指明參數(shù)的話,就退化到 Perl 5 一樣使用 $_.

我們可以意識(shí)到這個(gè)我們能做什么,比如迭代一個(gè)列表袍睡。當(dāng)然,也可以是一個(gè)數(shù)組的值.

for @array { .say }

這是一個(gè)非常簡單的例子,我們可能更加喜歡使用 map:

@array.map : *.say;

如果對你來講 順序 和連續(xù)的并不重要,你可以使用 hyperoperator(超運(yùn)算符),上一個(gè)文章中也講過這個(gè),今天的主題也不詳細(xì)講這個(gè)了.

 @array?.say;

我們也能使用 范圍構(gòu)造器中綴操作符 .. 來生成一個(gè)數(shù)字的列表:

  for 1..4 { .say }

有一個(gè)最通用的功能,就是我們想些生成一個(gè)從 0 開始到 $n 的數(shù)字的列表,比如常用的數(shù)組下標(biāo).我們可以寫成 0 .. $n-1或者另一個(gè)不同的范圍構(gòu)造器 0..^$n.但在 Perl 6 中提供了一個(gè)短的快捷的方法就是使用前綴的 ^.

for ^4 { .say }

0
1
2
3

一個(gè)常用的理由是,人們在 Perl 5 中常常退回到 C 風(fēng)格的循環(huán)的原因是必須知道 for 的成員數(shù)組中索引的位置,或者因?yàn)楸仨毑⑿械牡€(gè)和更多的數(shù)組.Perl 6 提供了一個(gè)短的快捷方法,就是中綴的 Z 這個(gè) zip 操作符.

for @array1 Z @array2 -> $one , $two { ... }

假設(shè)二個(gè)數(shù)組是相同的長度$one 會(huì)是第一個(gè) @array1 的成員元素,$two 會(huì)是相應(yīng)的位置 @array2 的成員元素.如果是不同的長度的話.迭代會(huì)停止到短的那個(gè)數(shù)組結(jié)束的長度.
我們可以很容易地在迭代數(shù)組包含進(jìn)索引:

for ^Inf Z @array -> $index , $item { ... }

如果一個(gè)無限長的列表,會(huì)讓你害怕使用上面用法的話,可以象下面這樣,使用前綴操作符 ^ 來取出數(shù)組元素的長度.

for ^@array.elems Z @array -> $index , $item { ... }

上面這個(gè)可以得到相同的結(jié)果,但是更加優(yōu)雅.因?yàn)橹芯Y操作符 Z 操作時(shí),第一個(gè)元素的長度決定了什么整個(gè)長度.

for @array.kv -> $index , $item { ... }  

@array.kv 會(huì)返回 keysvalues 的交錯(cuò),這個(gè) $key 是數(shù)組元素的下標(biāo).所以同時(shí)迭代這二個(gè)可能是你比較想要的效果.
希望這篇文章讓你了解 Perl 6 靈活的循環(huán)相關(guān)的一些概念,它們可以靈活的使用在各種常見任務(wù)上.在這之前,我要回答最后一個(gè)問題,我知道有人一直在想這個(gè)問題.怎么樣一次性迭代四個(gè)數(shù)組.

for @one Z @two Z @three Z @four -> $one , $two , $three , $four { ... }

這是一個(gè)關(guān)聯(lián)列表中綴操作符,這樣使用,是不是一種享受脉课?

超運(yùn)算符


pmichaud 在昨天介紹了 Perl 6 的 hyper 運(yùn)算符,我這要進(jìn)一步來探索 Perl6 中強(qiáng)大的元操作的特性.
首先,為簡單起見,我將編寫一個(gè) lsay 的函數(shù),可以輕松地得到好看的列表值的輸出.這個(gè) sub 是用我們用 Perl 來創(chuàng)建的

our sub lsay( @a ) { @a.perl.say }

接下來我們看 hyperoperator 的例子.在這個(gè)中,我們使用 >><< 來替換 ??, 主要因?yàn)檫@樣更加容易看(我怕我會(huì)需要眼鏡). ?? 是語言中真實(shí)的形式,但較長的 ASCII 字符版本也是可以正常工作的.

首先.來個(gè)基本的:
添加兩個(gè)相同長度的列表

> (1, 2, 3, 4) <<+>> (3, 1, 3, 1)
4, 3, 6, 5
> (1, 2, 3, 4) >>+<< (3, 1, 3, 1)
4, 3, 6, 5

如果數(shù)組的長度是相同的,上面這兩種形式之間沒有區(qū)別.但是,如果長度是不同的:

> (1, 2, 3, 4) <<+>> (3, 1)
4, 3, 6, 5
> (1, 2, 3, 4) >>+<< (3, 1)
Sorry, lists on both sides of non-dwimmy hyperop are not of same length : left: 4 elements, right: 2 elements

這規(guī)則是, 象諸如此類的尖尖是用來表明 hyperoperator 使用時(shí),當(dāng)一端比另一端短,可以延長短的那一端來進(jìn)行擴(kuò)展延伸.

象如果是尖尖指向內(nèi)部,是指不能進(jìn)行擴(kuò)展延伸.當(dāng)然,還可以有各種組合都是可以的.所以你也能指出只有左邊能擴(kuò)展延伸 (<<+<<),也可以只指出只有右邊能(>>+>>).當(dāng)然也能二邊都是可以擴(kuò)展延伸 (<<+>>),或者二邊都不能擴(kuò)展延伸 (>>+<<). R 語言中也有向量的循環(huán)法則。
單標(biāo)量擴(kuò)展延伸如下:

> (1, 2, 3, 4) >>+>> 2
3, 4, 5, 6
> 3 <<+<< (1, 2, 3, 4)
4, 5, 6, 7

因此,這就是基本的使用中綴操作符 hyperoperator 的方法.您還可以使用前綴和后綴運(yùn)算符:
單邊運(yùn)算時(shí)仔雷,元素要能漏到操作符的左邊( 如@a>>++ )或右邊( 如 ~<< )冷溶。想象一下漏斗,總是從大的口向小的口漏糯钙。
所以操作符前面或后面接什么樣的超運(yùn)算符要取決于操作數(shù)是在操作符的前面(用>>)或后面(用<<)

> ~<<(1, 2, 3, 4)    # ~(1,2,3,4)

超運(yùn)算符就是在普通運(yùn)算符的后面粪狼,加強(qiáng)普通運(yùn)算符的功能。如

> ~<< "1" , "2" , "3" , "4"
> -<<(1, 2, 3, 4)
-1 -2 -3 -4
> my @a = (1, 2, 3, 4);
@a>>++;
@a ;    # 單邊運(yùn)算時(shí)任岸,@a與>>之間不能有空格再榄,如不能寫成@a >>+
2, 3, 4, 5

你也能這樣:

> (0, pi/4, pi/2, pi, 2*pi)>>.sin  # R 中向量化的運(yùn)算
0, 0.707106781186547, 1, 1.22464679914735e-16, -2.44929359829471e-16
> (-1, 0, 3, 42)>>.Str
"-1" , "0" , "3" , "42"

這其實(shí)就是只是想說 >>. 是調(diào)用列表中的每一個(gè)成員的一種方法。點(diǎn) (.)也是一個(gè)操作符

其他說明:hyperoperators 并不只是只能和內(nèi)置操作符一起工作.他們也將能跟你定義以及任何新的運(yùn)算符工作的很好(即大多數(shù)的的都能正常在現(xiàn)在的 Rakudo 上工作).

只要給放在適當(dāng)?shù)牡胤?如@a >>/=>>2 整個(gè)數(shù)組成員都除以 2. 他們將來能和更多的結(jié)構(gòu)一起工作,如多維列表,樹與哈希;我們可 S03 Hyper operators .(據(jù)我所知,有些功能還尚未在 Rakudo 正常實(shí)現(xiàn))

我并不知道是否有很多代碼示例中廣泛使用 hyperoperators. 但 LastOfTheCarelessMen’s Vector 是一個(gè)非常好的實(shí)現(xiàn).它使用單循環(huán)直接的實(shí)現(xiàn)了一個(gè) N 維向量類.

N維向量

reduce 和 hyper 元操作符


Hyper 亢奮的享潜;精力旺盛的 Hyper[?ha?p?(r)]

今天是第四天,在這個(gè)小盒子中,你會(huì)見到一些有意思的實(shí)現(xiàn)階乘的函數(shù)

sub fac( Int $n ) {
        [*] 1.. $n
}

Okay, 它是怎么工作的困鸥? 今天的 Advent 的盒子就是為了給你提供答案.
Perl 6 有一些不同的"元操作符"是用來修改現(xiàn)有的運(yùn)算符完成更加強(qiáng)大的功能.
這個(gè)方括號(hào)中是一個(gè)有關(guān)“reduce metaoperator”的元操作符的例子,它是中綴運(yùn)算符,會(huì)變成列表操作,操作是在后面各個(gè)元素的中間來, 例如,表達(dá)式

 [+] 1, $a , 5, $b

它相當(dāng)于

   1 + $a + 5 + $b

這為我們提供了非常便利的機(jī)制“計(jì)算整個(gè)列表中的所有元素之和”:

$sum = [+] @a ; # @a 中所有元素之和

更多的中綴運(yùn)算符(包含用戶自己定義的),都能放到這個(gè)方括號(hào)來減少操作符;

$prod   = [*] @a ;         # 相乘 @a 中所有的元素
$mean   = ([+] @a ) / @a ; # 計(jì)算 @a 的平均值
$sorted = [<=] @a ;        # 如果 @a 元素是數(shù)字排序就為 true
$min    = [min] @a , @b ;  # find the smallest element of @a and @b combined

在那個(gè)階乘的子函數(shù)中,表達(dá)示 [*] 1..$n 返回全部 1$n 之間所有乘數(shù)的乘積.

另一個(gè)非常有用的元操作符是 "hyper" 操作符,放置 >>(與|或)<< 在操作符的二邊(一邊),使得那個(gè)操作 "hyper"(更亢奮).這個(gè)是用來操作列表中所有的成員,來進(jìn)行這個(gè)包起來的運(yùn)算符的操作.象下面的例子,我們來打算從 @a 和 @b 中成對的取出數(shù)據(jù)來進(jìn)行運(yùn)算后存入 @c.

@c = @a >>+<< @b ;

如果是在 Perl 5 中,我們需要寫成象才面這樣才能完成.

for ( $i = 0; $i < @a ; $i ++) {
        $c [ $i ] = $a [ $i ] + $b [ $i ];
}

這只是有點(diǎn)長.

正如上面的方括號(hào)中,我們可以使用Hyper作用在各種運(yùn)算符上,包括用戶定義操作符:
注:可以這樣記憶 <<>> 操作符,它們就像是漏斗剑按,<< 讓元素從右邊漏入疾就,>> 讓元素從左邊漏入,然后進(jìn)行運(yùn)算艺蝴。

# 對 @xyz 中所有的元素進(jìn)行 ++ 的操作
@xyz >>++
# 從@a 和 @b 中找出最小的元素放到 @x 中
@x = @a   >>min<< @b ;

我們還可以翻轉(zhuǎn)<<的角度,使標(biāo)量的行為像一個(gè)數(shù)組

# @a 中每個(gè)成員都乘 3.5  
my @a=2,4,6;
@b = @a   >>*>> 3.5;  

這其實(shí)相當(dāng)于 @b = @a >>*<< (3.5,3.5,3.5) 較短的向量會(huì)被自動(dòng)循環(huán)使用猬腰!模仿 R 語言的短向量自動(dòng)循環(huán)。
如果右邊的向量沒有左邊的長猜敢,箭頭就指向那個(gè)單個(gè)向量姑荷。

# @x 中每個(gè)成員都乘以 $m 然后在加 $b
@y = @x   >>*>> $m >>+>> $b ;
# 顛倒 @x 中所有的成員
@inv = 1  <</<< @x ;
# concatenate @last, @first to produce @full
@full = ( @last   >>~>> ', ' )  >>~<< @first ;
> my @string=<I LOVE YOU>
I LOVE YOU
> @string >>~>>'-' >>~>> "szx"
I-szx LOVE-szx YOU-szx

>>~<< 兩側(cè)的元素個(gè)數(shù)必須相同!
當(dāng)然,reductions 和 hyper 操作符也能聯(lián)合表達(dá)式

  # 計(jì)算 @x 的平方和
  $sumsq = [+] ( @x   >>**>> 2);

還有很多其他元操作符,包括X(cross交叉),R(reverse反向),S(順序sequential).事實(shí)上,這只是在恰當(dāng)?shù)奈恢梅艂€(gè)運(yùn)算符,如+=,*=,?=,只是元形式的后綴等號(hào)運(yùn)算,它相當(dāng)于:

$a += 5;     # same as $a = $a + 5;
$b = 7;      # same as $b = $b 7;
$c min= $d ; # same as $c = $c min $d;  

static types 和 multi subs.


打開Advent 這第三個(gè)盒子,這次我們要讀到什么啦缩擂?啊….真好.這次沒想到有二個(gè)禮物.這個(gè)盒子中放著 static typesmulti subs.
在 Perl 5 中,$scalar 的標(biāo)量只能包含二種東西引用或值,這值可以是任何東西,能是整數(shù),字符,數(shù)字,日期和你的名字.這通常是非常方便的,但并不明確.
在 Perl6 中給你機(jī)會(huì)修改標(biāo)量的類型 .如果你這個(gè)值比較特別,你可以直接放個(gè)類型名在 my$variable 的中間.象下面的例子,是在設(shè)置値一定要是一個(gè) Int 型的數(shù)據(jù),來節(jié)約 cpu 判斷類型的時(shí)間,和讓你更少的程序上出錯(cuò).

my Int $days = 24;

其它的標(biāo)量類型如下:

my Str $phrase   = "Hello World" ;
my Num $pi       = 3.141e0;
my Rat $other_pi = 22/7;

如果你還是想用老的設(shè)置值的方法,你可以不聲明類型或使用 Any 的類型代替.

今天盒子中的第二個(gè)禮物 multi subs 也很容易,因?yàn)槲覀儠?huì)手把手教你.到底什么是 multi subs ? 簡單的來講 multi subs 可以讓我們 重載 sub 的名字 .當(dāng)然 Multi subs 可以做更多其它的事情,所以下次其它作者的禮物中也會(huì)有這個(gè),但現(xiàn)在我們會(huì)先介紹幾個(gè)非常有用的一些 sub .

multi sub identify(Int $x) {
    return "$x is an integer.";
}

multi sub identify(Str $x) {
    return qq<"$x" is a string.>;
}

multi sub identify(Int $x, Str $y) {
    return "You have an integer $x, and a string \"$y\".";
}

multi sub identify(Str $x, Int $y) {
    return "You have a string \"$x\", and an integer $y.";
}

multi sub identify(Int $x, Int $y) {
    return "You have two integers $x and $y.";
}

multi sub identify(Str $x, Str $y) {
    return "You have two strings \"$x\" and \"$y\".";
}

say identify(42);
say identify("This rules!");
say identify(42, "This rules!");
say identify("This rules!", 42);
say identify("This rules!", "I agree!");
say identify(42, 24);

還有兩個(gè)禮物很有優(yōu)勢吧.你可以嘗試多使用他們,我們會(huì)不斷的豐富這個(gè) Advent 的樹,并不斷放更多的禮物,希望你能多來看看.

.comb your constraints


我們以前 advent 了解過的內(nèi)容,對于今天所要介紹的禮物非常有用,今天要講兩個(gè)東西: comb 方法和 constraints 的概念鼠冕。

constraints 和原來那章節(jié)中提到的靜態(tài)變量定義的相同,constraints 可以讓我們在寫程序的時(shí)候就更方便的在子函數(shù)和方法上進(jìn)行控制.

在很多其它的程序中,你可以通過參數(shù)調(diào)用子函數(shù)并可以在參數(shù)進(jìn)入的時(shí)候就通過 constraints 來驗(yàn)證輸入的內(nèi)容.這樣我們就能在程序聲明的時(shí)候就驗(yàn)證輸入的內(nèi)容,不在需要等到程序運(yùn)行的時(shí)候.

下面是一個(gè)基本的例子,如果是一個(gè)整數(shù)和偶數(shù),在子函數(shù)中它會(huì)不能處理下去.在 Perl 5 中的實(shí)際基本就象下面這樣子了:

 sub very_odd
 {
    my $odd = shift;
    unless ($odd % 2)
    {
        return undef;
    }
    # 在這接著處理奇數(shù).
 }

在 Perl 6 中,我們可以只需要簡單的:

  sub very_odd(Int $odd where {$odd % 2})
  {
        # 在這接著處理奇數(shù).
  }

如果你試圖來傳入一個(gè)偶數(shù)來調(diào)用 very_odd.你會(huì)直接得到一個(gè) error.不要擔(dān)心:你可以使用 multi sub 的功能來給偶數(shù)一個(gè)機(jī)會(huì):

  multi sub very_odd(Int $odd where {$odd % 2})
   {
       # Process the odd number here
  }
  multi sub very_odd(Int $odd) { return Bool::False; }

我們在使用成對的 .comb 方法時(shí),這個(gè) constraints 是非常有用.

為什么正好是 .comb ? 當(dāng)我們早上梳整我們的頭發(fā)時(shí),我們先通常使用梳子來梳成你想要的樣子(線條),然后在你的頭上固定梳成的樣子.前面講的內(nèi)容在這非常象.split.在這也一樣,你不是真想要切開字符串,而是你想達(dá)到一個(gè)什么樣目的.這一段簡單的代碼,來說明這兩種目標(biāo):

  >say "Perl 6 Advent".comb(/<alpha>/).join('|');
  P|e|r|l|A|d|v|e|n|t
  >say "Perl 6 Advent".comb(/<alpha>+/).join('|');
  Perl|Advent

正則表達(dá)式有可能另一天會(huì)拿出來講,但是我們先快速了解一下是沒有壞處的.這個(gè)第一行,會(huì)輸出 P|e|r|l|A|d|v|e|n|t. 它會(huì)取得每個(gè)字母然后放到一個(gè)暫時(shí)的數(shù)組中,然后使用 join 管道連接起來這是目的.第二行也有點(diǎn)象,但它捕獲了更多的字母,會(huì)輸出 Perl|Advent 這是第二個(gè)的目標(biāo)單詞.
這個(gè) .comb 是非常非常強(qiáng)大,然而,你得到你梳出來的輸出,你就能操作這個(gè)串了如果你有一個(gè)基本的ASCII十六進(jìn)制字符的字符串,可以使用的 hyperoperators 超的操作符轉(zhuǎn)變各自的塊成為等效的 ASCII 字符!

say "5065726C36".comb(/<xdigit>**2/)?.fmt("0x%s")?.chr
# Outputs "Perl6"
say "5065726C36".comb(/<xdigit>**2/)
50 65 72 6C 36

**在正則里是量詞撇叁,表示重復(fù)前面的十六進(jìn)制數(shù)兩次供鸠,合起來就是每兩個(gè)字符分一下。

如果你提心這個(gè),你可以使用 .map 的方法:

say "5065726C36".comb(/<xdigit>**2/).map: { chr '0x' ~ $_ } ;
#Outputs "Perl6"

記的,這是 Perl.做任何事情都不只一種方法.
今天給完了所有禮物,我現(xiàn)在向你挑戰(zhàn).有 KyleHasselbacher 的協(xié)助,我們能使用約束.comb.map 做出一個(gè)像樣的版本的古老的凱撒加密法.

 use v6;

 sub rotate_one( Str $c where { $c.chars == 1 }, Int $n ) {
    return $c if $c !~~ /<alpha>/;
    my $out = $c.ord + $n;
    $out -= 26 if $out > ($c eq $c.uc ?? 'Z'.ord !! 'z'.ord);
    return $out.chr;
 }

 sub rotate(Str $s where {$s.chars}, Int $n = 3)
 {
    return ($s.comb.map: { rotate_one( $_, $n % 26 ) }).join( '' );
 }

 die "Usage:\n$*PROGRAM_NAME string number_for_rotations" unless @*ARGS == 2;

 my Str $mess   = @*ARGS[0];
 my Int $rotate = @*ARGS[1].Int;

 say qq|"$mess" rotated $rotate characters gives "{rotate($mess,$rotate)}".|;

我希望你在休息的時(shí)候,可以使用目前為止在 Perl 6 中和今天的禮物中的學(xué)到的內(nèi)容來編寫編碼算法.畢竟,編程語言本身只有更多的使用,才能讓它變的更優(yōu)秀.

一個(gè)正則表達(dá)式的故事


By perlpilot

在 advent 的第十天,我們有一個(gè)故事做為禮物……

曾幾何時(shí),在比你想象的更近的時(shí)候,一個(gè)叫 Tim 的學(xué) Perl 6 程序的學(xué)生,工作中出現(xiàn)了一個(gè)簡單的解析相關(guān)的問題.他的老板(我們叫他 C 先生)曾問過他,解析日志文件中包含著庫存信息,確保在文件內(nèi)是唯一有效的行.文件中每行內(nèi)是這樣的:
<part number> <quantity> <color> <description>

所以這個(gè) Perl 6 的學(xué)生,他用熟悉正則表達(dá)式寫了一個(gè)可愛的小正則表達(dá)式,可以用來找出有效的行.代碼檢查每行內(nèi)容是這樣寫的:

next unless $line ~~ / ^^ \d+ \s+ \d+ \s+ \S+ \s+ \N* $$ /

使用 ~~ 操作符的原因是因?yàn)?右側(cè)的正則表達(dá)式會(huì)匹配左側(cè)標(biāo)量.在正則內(nèi)部,^^ 是匹配行的開頭,\d+ 是用來匹配一個(gè)或者多個(gè)數(shù)字(由零件編號(hào) part number 和數(shù)量 quantity 組成的),\S+ 是用來匹配一個(gè)或者多個(gè)非空白字符,
\N* 來匹配零個(gè)或者多個(gè)非換行符,\s+ 匹配空白之間的這些東西和 $$ 用來匹配行結(jié)束.
在 Perl 6 中,正則表達(dá)式的每個(gè)單獨(dú)的部分可以使用空格來讓它更具可讀性,所以更加好,這個(gè)空格不會(huì)是正則的一部分只用來分隔.

但 C 先生決定最好信息的每個(gè)部分都可以從提取來驗(yàn)證. Tim 想了一下,“沒問題,我只要使用括號(hào)來捕獲”.下面就是全部需要做的:

next unless $line ~~ / ^^ (\d+) \s+ (\d+) \s+ (\S+) \s+ (\N*) $$ /

在成功的模式匹配以后,每個(gè)括號(hào)內(nèi)都存著匹配到的對象本身($/),可以通過 $/[0],$/[1],$/[2]$/[3].它可以通過特殊的變量 $0,$1,$2,$3訪問.Tim 和他老板 C 先生都很高興.

但隨后發(fā)現(xiàn)了一些行中,沒有從描述信息中給顏色信息分開,這些行其實(shí)也是有效的.在行中顏色信息和描述信息有個(gè)特殊的組合方式.他們總是象下面這樣:

<part number> <quantity> <description> (<color>)

在這像以前一樣,可以加入包括任意數(shù)量的空格在字符中. Tim 認(rèn)為,“現(xiàn)在這個(gè)本來簡單的解析程序似乎突然更加復(fù)雜了”.幸運(yùn)的是,Tim 可以找一個(gè)地方尋求幫助.他迅速登錄到 irc.freenode.org,加入 #perl6 通道 并請求大家協(xié)助.有人建議他使用名字來命名他的正則表達(dá)式的各個(gè)部分,來使事情變得更容易.然后使用交替的方法來匹配這個(gè)正則表達(dá)式的最后一部分的多種可能.

首先,Tim 嘗試給正則能捕獲到的每個(gè)部分都加上一個(gè)名字,詳細(xì)信息可以見 Perl 6 正則的綱要,下面是他所做的:

next unless $line ~~
    / ^^ $<product>=(\d+) \s+ $<quantity>=(\d+) \s+ $<color>=(\S+) \s+ $<description>=(\N*) $$ /

現(xiàn)在,成功的匹配后,每各部分都可以匹配到對象中不同的東西,通過特殊的變量 $<Product>,$<quantity>,$<color>$<description>.
這比預(yù)期的更容易,讓 Tim 感到非常有信心.接著,他需要補(bǔ)充:交替區(qū)分兩種不同的有效行:

next unless $line ~~ / ^^
    $<product>=(\d+) \s+ $<quantity>=(\d+) \s+
    [
    | $<description>=(\N*) \s+ '(' $<color>=(\S+) ')'
    | $<color>=(\S+) \s+ $<description>=(\N*)
    ]
  $$
/

為了從正則表達(dá)式中的交替和其余部分隔離開,Tim 使用了分組括號(hào)([ and ])在要交替檢查的部分.
這個(gè)分組是正則的一部分,其中像圓括號(hào)是唯一沒有捕捉到 $0 的, 由于必須匹配到精確的圓括號(hào), Tim 使用了另一個(gè)有用的 Perl6 正則表達(dá)式的優(yōu)勢:帶引號(hào)的字符串字面匹配.因?yàn)榉峙浣o正則表達(dá)式的中 $<color>$<description> 總是會(huì)在適當(dāng)部分包含字符串.

Tim 非常的揚(yáng)眉吐氣陨闹!他展示了他的代碼給 Mr.C,并表揚(yáng)到 "干得好 Tim楞捂!";

然而,經(jīng)過成功過后,Tim 開始以更挑剔的眼光來看他的工作.對于一些行中描述之后顏色,它有可能是 ( color) or (color ) or( color ).他目前正則表達(dá)式是正常的,但如果描述中包括的顏色的部分象前面一樣時(shí),并不是所有匹配顏色的會(huì)設(shè)置 $<color>. Tim 初步修復(fù),通過加入更多的 \s*

next unless $line ~~ / ^^
    $<product>=(\d+) \s+ $<quantity>=(\d+) \s+
    [
    | $<description>=(\N*) \s+ '(' \s* $<color>=(\S+) \s* ')'
    | $<color>=(\S+) \s+ $<description>=(\N*)
    ]
  $$
/

這運(yùn)行的非常良好,但正則表達(dá)式的開始顯得有點(diǎn)凌亂.Tim 再次使用 #perl6 來讓人幫助.

這時(shí)候有個(gè)名叫 PerlJam 告訴他,“你為什么不把你的正則表達(dá)式放到 grammar 中?這可以讓你分配給每片到變量來匹配對象”“ Wha?? Tim 不知道 PerlJam 講的是什么.通過簡短的交流后,Tim 了解后,并知道在哪里查看必須的相關(guān)信息后.然后感謝 PerlJam,并在次回到了程序上.這一次的正則表達(dá)式幾乎消失,因?yàn)樗褂昧?grammar.什么是 grammar 趋厉?,看下面匹配的代碼:

grammar Inventory {
    regex product     { \d+ }
    regex quantity    { \d+ }
    regex color       { \S+ }
    regex description { \N* }
    regex TOP { ^^ <product> \s+ <quantity>  \s+
                [
                | <description> \s+ '(' \s* <color> \s*  ')'
                | <color> \s+ <description>
                ]
                $$
    }
}
# ...在來到代碼開始的地方
next unless Inventory.parse($line);

以前的正則表達(dá)式中各自的變量變成了 grammar 中的命名正則表達(dá)式.在 Perl 6 的正則表達(dá)式中的命名正則是由括在尖括號(hào)內(nèi)的名稱來匹配(< and >).當(dāng) Grammar.parse 調(diào)用來匹配一個(gè)標(biāo)量時(shí)(會(huì)操作這特定的命名正則 TOP)行為是完全和以前一樣,因?yàn)槊恼齽t表達(dá)相當(dāng)于其它正則表達(dá)式的一部分,匹配的文本保存到匹配對象中,并引用該名稱.

雖然仍然有改進(jìn)的余地,Tim 和 Mr.C 對這個(gè)結(jié)果感到非常高興.


注:默認(rèn)情況下,允許啟用空格注解寨闹; 所以,雖然在 Perl 5 中您可以用“hello there”本身來匹配“hello there”,但在 Perl 6 中,您必須將其改為 /hello <sp> there/.這樣就可以在正則表達(dá)中將條件清晰地分離開來.

Perl 6 正則表達(dá)式可以被復(fù)用.在匹配單一的詞時(shí),復(fù)用正則表達(dá)式是很荒謬的;但在解析配置文件時(shí),幾乎必須要復(fù)用正則表達(dá)式(這取決于配置文法的復(fù)雜度君账、發(fā)生修改的頻率等).這樣性能也會(huì)高很多.

在 Perl 5 中, Regexp::Common 模塊,已經(jīng)在嘗試復(fù)用正則表達(dá)式,但是,因?yàn)?Perl 5 不允許復(fù)用正則表達(dá)式,所以不得不將它們封裝在一個(gè)模塊接口中. Perl 6 完全支持這種復(fù)用.
其它參數(shù)資料:

類, 屬性, 方法和其它


By jnthnwrthngtn

我非常興奮地撕下今天的禮物上閃亮的包裝紙,里面是無可爭議的 Perl 6 的對象模型,它內(nèi)置了其類聲明,角色組成,自豪的元模型(meta-model).除了有先進(jìn)的功能外,讓我們看看在 Perl 6 中是多么容易寫一個(gè)類.

class Dog {
    has $.name;
    method bark($times) {
        say "w00f! " x $times;
    }
}

我們開始使用一個(gè) class 的關(guān)鍵字.如果你有學(xué)過 Perl5 的話,你能想到的類有點(diǎn)像包(package)的變種,這個(gè)關(guān)鍵字為您提供一個(gè)優(yōu)雅的語義.

接下來,我們使用 has 的關(guān)鍵字來聲明屬性訪問器方法.這個(gè)"."的東西名叫 twigil. Twigil 是用來告訴你指定變量的作用域.它是"屬性 + 存取方法"的組合.它的選項(xiàng)是:

has $!name;       # 私有; 只能在內(nèi)部可見
has $.name is rw; # Generates an l-value accessor

接下來是方法的使用,并介紹使用 method 的關(guān)鍵字.在對象中的方法象包中的子函數(shù),不同之處在于方法是放在類的方法列表的條目中.
它還能自動(dòng)取得調(diào)用者(invocant),所以你如果沒有在參數(shù)列表中加入?yún)?shù).它是會(huì)給自我傳遞過去.在 Perl 5 中需要我們顯示的寫 $self = shift.

所有的類都繼承一個(gè)叫 new 的默認(rèn)的構(gòu)造器,會(huì)自動(dòng)的映射命名參數(shù)到屬性,所有傳進(jìn)的參數(shù)會(huì)存到屬性中.我們可以調(diào)用 Dog 的構(gòu)造器(這個(gè) Dog 的類的對象,并取得一個(gè)新的實(shí)例).

my  $fido = Dog.new(name => 'Fido');
say $fido.name;  # Fido
$fido.bark(3);   # w00f! w00f! w00f!

請注意,Perl 6 中的方法調(diào)用操作符是"."而不是 Perl 5 中使用的"->".它縮短了 50% 并更加合適從其他語言轉(zhuǎn)過來的開發(fā)人員.

當(dāng)然,很容易實(shí)現(xiàn)繼承,下面我們建一個(gè)叫 puppy 子類 ,直接使用 is 加父類的名字就行了.

class Puppy is Dog {
    method bark($times) {
        say "yap! " x $times;
    }
}

這也支持委托,詳細(xì)作用見下面的 FQA.

class DogWalker {
    has $.name;
    has Dog $.dog handles (dog_name => 'name');
}
my $bob = DogWalker.new(name => 'Bob', dog => $fido);
say $bob.name;      # Bob
say $bob.dog_name;  # Fido

在這里,我們聲明指出我們想調(diào)用 DogWalker 類的名為 dog_name 的方法,并設(shè)置這個(gè)方法轉(zhuǎn)到 Dog 類中包含名為 name 的方法.重命名只是其中的一個(gè)可選方式;委托常常有很多其它的實(shí)現(xiàn)方法.

內(nèi)心深層之美比外在更加重要.所以,在整潔的語法之下是使用 meta-model(元模型)想法來實(shí)現(xiàn)對象.類,屬性和方法都是 Perl 6 中最重要和 Meta-object 的.我們可以在運(yùn)行時(shí)使用這些內(nèi)省對象.

for Dog.^methods(:local) -> $meth {
    say "Dog has a method " ~ $meth.name;
}

這個(gè) .^ 的操作是 . 操作的變種,用來替換元類(metaclass-描述類的這個(gè)對象)的調(diào)用.在這里,我們提供該類所定義的方法(Method)的列表,我們使用 :local 來排除那些從父類的繼承. 這不只是給我們一個(gè)名字列表,而是方法對象的列表.其實(shí)我們直接使用這個(gè)對象來調(diào)用方法,但在這種情況下,我們只要它的名字就行.

讓你了解 Meta-programming 并附送一個(gè)擴(kuò)展 Perl6 的對象的功能:只要你知道聲明一個(gè)方位,使用 method 的關(guān)鍵字讓它在編譯時(shí)在調(diào)用元類中的 add_method 來變成實(shí)際的方法.所以在 Perl 6 中,不僅為您提供了強(qiáng)大的對象模型,但也提供了機(jī)會(huì),用來實(shí)現(xiàn)其它的特性,以滿足未來我們還沒有想到的需求.

這些都只是 Perl 6 的對象模型所提供的偉大的事情中的一些,也許我們會(huì)發(fā)現(xiàn)更多的東西在其他禮品中. :-)

注:
面向?qū)ο蟮母拍?/p>

首先,我們定義幾個(gè)預(yù)備性的術(shù)語.

構(gòu)造器 (constructor):   創(chuàng)建一個(gè)對象的函數(shù).
實(shí)例 (instance):  一個(gè)對象的實(shí)例化實(shí)現(xiàn).
標(biāo)識(shí) (identity):  每個(gè)對象的實(shí)例都需要一個(gè)可以唯一標(biāo)識(shí)這個(gè)實(shí)例的標(biāo)記.
實(shí)例屬性 (instance attribute):  一個(gè)對象就是一組屬性的集合.
實(shí)例方法 (instance method):  所有存取或者更新對象某個(gè)實(shí)例一條或者多條屬性的函數(shù)的集合.
類屬性(class attribute):  屬于一個(gè)類中所有對象的屬性,不會(huì)只在某個(gè)實(shí)例上發(fā)生變化.
類方法(class method):  那些無須特定的對性實(shí)例就能夠工作的從屬于類的函數(shù).
委托 (Delegation):  在對象需要執(zhí)行某個(gè)工作時(shí)并不直接處理繁堡,而是要求另一個(gè)對外象代為處理(有時(shí)只處理部分工作),所以這時(shí)第二個(gè)對象代表第一個(gè)對象來執(zhí)行該操作。
調(diào)用者(invocant):   對類來講,調(diào)用者是包的名字,對實(shí)例方法來講,調(diào)用者是指定對象的引用.換句話講,調(diào)用者就是調(diào)用方法的那種東西,有的文章叫他為代理(agent)施動(dòng)者(actor).
抽象類(abstract class):抽象類實(shí)現(xiàn)類的占位符,主要用來定義行為椭蹄,而子類用來實(shí)現(xiàn)這個(gè)行為闻牡。

arguments and parameters


By carl

在第9天的 advent 中…我打開了 …這是有關(guān) parameters 和 arguments
你也許了解或者不了解 Perl5 的 是怎么處理函數(shù)參數(shù)的.先讓你看看,它通常象下面的這個(gè)例子這樣:

sub sum {
   [+] @_
}
say sum 100, 20, 3; # 123

這個(gè) [+] 是在 Perl 6 中的,但我們也可以寫成 Perl 5 風(fēng)格的

my $i = 0;
$i _= $_ for @_;
$i;

我們要想到上面這些區(qū)別,這些在 Perl 6 中非常重要,也就是為什么我們講 Perl 6 比 Perl 5 好.當(dāng)你調(diào)用函數(shù)時(shí).你可以從 @_ 找到你的參數(shù).你然后取出它們來做一些操作.
這是非常靈活的.因?yàn)樗粫?huì)對參數(shù)做任何默認(rèn)的處理,程序會(huì)全部傳給你來進(jìn)行處理.當(dāng)然這也同樣是令人厭煩因?yàn)闃訕佣家约禾幚?但很方便我們來進(jìn)行擴(kuò)展進(jìn)行參數(shù)的檢查,看下面這個(gè)虛構(gòu)的例子.

sub grade_essay {
  my ($essay, $grade) = @_;
  die 'The first argument must be of type Essay'
    unless $essay ~~ Essay;
  die 'The second argument must be an integer between 0 and 5'
    unless $grade ~~ Int && $grade ~~ 0..5;

  %grades{$essay} = $grade;
}

(如果在 Perl 5 中,你需要使用 isa 來替換 ~~ 和使用 %grades 來替換成 $grades 才能正常工作.除了這些,都在 Perl6 中工作)

現(xiàn)在,這一刻,看看上面的內(nèi)容,看到手冊中的參數(shù)驗(yàn)證的實(shí)現(xiàn),你是不是開始有點(diǎn)絕望嗎?你感覺到了吧绳矩?好.
在 Perl 5 中的解決方法是使用優(yōu)秀的 CPAN 模塊,象 Sub::SignaturesMooseX::Declare,然后在你的程序中使用這些模塊,并按照模塊設(shè)置就行了.

在 Perl 6 的中的解決方法是,給你參數(shù)設(shè)置默認(rèn)范圍. 我在想看了下面這些時(shí), “請確保鍵盤前的你不會(huì)流口水”.在 Perl 6 中,我會(huì)寫這樣來寫子函數(shù):

sub grade_essay(Essay $essay, Int $grade where 0..5) {
  %grades{$essay} = $grade;
}

現(xiàn)在我們見到,在這程序運(yùn)行會(huì)對這個(gè)長版本的參數(shù)進(jìn)行檢查,沒有必要在導(dǎo)入其它的 CPAN 的模塊了.

有時(shí),我們可以提供一些默認(rèn)的值給參數(shù):

sub entreat($message = 'Pretty please, with sugar on top!', $times = 1) {
    say $message for ^$times;
}

如果這些參數(shù)的默認(rèn)的值是不固定的,可以使用老的方式來傳參數(shù).

sub xml_tag ($tag, $endtag = matching_tag($tag) ) {...}

如果您的參數(shù)是不確定的,對這種可選的參數(shù)可以加一個(gè) ? 的標(biāo)記.

sub deactivate(PowerPlant $plant, Str $comment?) {
  $plant.initiate_shutdown_sequence();
  say $comment if $comment;
}

有一個(gè)特性,我特別喜歡,我們可以在調(diào)用時(shí)通過參數(shù)名字來引用參數(shù),這樣你可以以喜歡的任何順序傳遞命名參數(shù).這樣會(huì)永遠(yuǎn)記得在這個(gè)函數(shù)中參數(shù)本來的順序:

sub draw_line($x1, $y1, $x2, $y2) { ... }

draw_line($x1, $y1, $x2, $y2); # phew. got it right this time.
draw_line($x1, $x2, $y1, $y2); # dang! :-/

這的方法是引用參數(shù)的名字,來使得這個(gè)問題被解決:

draw_line(:x1($x1), :y1($y1), :x2($x2), :y2($y2)); # works
draw_line(:x1($x1), :x2($x2), :y1($y1), :y2($y2)); # also works!

冒號(hào)的意思是 "這來自命名參數(shù)", 整個(gè)結(jié)構(gòu)讀作:name_of_parameter($variable_passed_in).這可以使用的參數(shù)和變量具有相同的名稱,但有一個(gè)簡短形式:

draw_line(:$x1, :$y1, :$x2, :$y2); # works
draw_line(:$x1, :$x2, :$y1, :$y2); # also works!

我喜歡短形式.我覺得它使我的代碼更具可讀性.

如果作為 API 的作者,要強(qiáng)迫別人使用命名參數(shù) – 例如還是在 draw_line 的情況下 – 你只需要提供在子程序參數(shù)前的冒號(hào).

sub draw_line(:$x1, :$y1, :$x2, :$y2 ) { ... } # optional nameds

但要小心注意,命名參數(shù)默認(rèn)是可選的.換句話說,上述內(nèi)容相當(dāng)于:

sub draw_line(:$x1?, :$y1?, :$x2?, :$y2?) { ... } # optional nameds

如果你想明確地指出必需的參數(shù),可以追加罩润!對下面的這些參數(shù):

sub draw_line(:$x1!, :$y1!, :$x2!, :$y2!) { ... } # required nameds

現(xiàn)在調(diào)用這個(gè),就像他們是普通的順序位置參數(shù)傳遞進(jìn)來.

關(guān)于可變參數(shù)呢?假如你想傳遞的參數(shù)是不確認(rèn)多少個(gè)數(shù)量,比如參數(shù)是數(shù)組,可以在它前面帶有“*”:

sub sum(*@terms) {
  [+] @terms
}
say sum 100, 20, 3;  # 123

我使用同樣的例子來提出一個(gè)觀點(diǎn):當(dāng)你不提供任何符號(hào)到您的子程序時(shí),你最終是得到的符號(hào)其實(shí)是是 *@_ .這是模擬 Perl 5 中的行為.

但數(shù)組前面的 * 號(hào)是僅用來捕獲的位置參數(shù)(positional arguments).如果你想捕捉命名參數(shù)(named arguments),你要使用 “slurpy hash”:

sub detect_nonfoos(:$foo!, *%nonfoos) {
  say "Besides 'foo', you passed in ", %nonfoos.keys.fmt("'%s'", ', ');
}

detect_nonfoos(:foo(1), :bar(2), :baz(3));
# Besides 'foo', you passed in 'bar', 'baz'

哦,這可能是一個(gè)很好的通過以命名的參數(shù)傳遞哈希的方法,像這樣:

detect_nonfoos(foo => 1, bar => 2, baz => 3);
# Besides 'foo', you passed in 'bar', 'baz'

這里的 Perl 5 中的一個(gè)重要區(qū)別:默認(rèn)參數(shù)是只讀的:

sub increase_by_one($n) {
  ++$n
}

my $value = 5;
increase_by_one($value); # boom

在這讓參數(shù)只讀,主要有兩個(gè)原因,其一為了效率.當(dāng)變量只讀時(shí)可以使其最佳化,其二要鼓勵(lì)程序員寫程序時(shí)有個(gè)正確的習(xí)慣,只會(huì)有一點(diǎn)點(diǎn)不習(xí)慣.
所以這個(gè)功能不僅是為優(yōu)化好,更是為了讓你有個(gè)更好的靈魂.

下面是你需要做的工作:

sub increase_by_one($n is rw) {
   ++$n
}

my $value = 5;
say increase_by_one($value); # 6

有時(shí)可能你想讓你的這個(gè)參數(shù)可以讀寫(RW),但是有時(shí)你可能更想修改傳進(jìn)來的參數(shù)復(fù)本.當(dāng)你想使用這個(gè) copy 時(shí):

sub format_name($first, $middle is copy, $last) {
    $middle .= substr(0, 1);
    "$first $middle. $last"
}

原內(nèi)容將保持不變.

在 Perl 6 中,當(dāng)傳遞一個(gè)數(shù)組或哈希時(shí),默認(rèn)情況下它并不會(huì)給數(shù)組和哈希拉平成幾個(gè)參數(shù).相反,當(dāng)你想讓參數(shù)扁平化時(shí)可以使用"|".

sub list_names($x, $y, $z) {
    "$x, $y and $z"
}

my @ducklings = <huey dewey louie>;
try {
    list_names(@ducklings);
}
say $!; # 'Not enough positional parameters passed;
# got 1 but expected 3'
say list_names(|@ducklings); # 'huey, dewey and louie'

同樣,如果扁平化一個(gè)哈希,其參數(shù)內(nèi)容將作為命名的參數(shù)(named arguments)發(fā)送到函數(shù).

正如您傳送數(shù)組和哈希一樣,你也可以傳送代碼塊:

sub traverse_inorder(TreeNode $n, &action) {
    traverse_inorder($n.left, &action) if $n.left;
    action($n);
    traverse_inorder($n.right, &action) if $n.right;
}

下面前三個(gè)印記符號(hào)(@ % & )其實(shí)是類型約束:

@ Array (actually, Positional)
% Hash (actually, Associative)
& Code (actually, Callable)

$ 的印記是工作在不受約束的版本.

當(dāng)心翼馆!常出的簡單的小陷阱是人們常常落入指定類型約束兩次,還都是同一個(gè)類型:

sub f(Array @a) { ... } # WRONG, unless you mean Array of Array
sub f( @a)      { ... } # probably what you meant
sub f(Int @a)   { ... } # Array of Int

你學(xué)到這,你應(yīng)得的另一個(gè) Perl6 單行…

$ perl6 -e '.fmt("%b").trans("01" => " #").say for <734043054508967647390469416144647854399310>.comb(/.**7/)'

Going to the Rats


As I hinted at back in the in the Day 1 post, Perl 6 has rational numbers. They are created in the most straightforward fashion, by dividing an integer with another integer. But it can be a bit hard to see that there is anything unusual about the result:

> say (3/7).WHAT
Rat()
> say 3/7
0.428571428571429

When you convert a Rat to a Str (for example, to “say” it), it converts to a decimal representation. This is based on the principle of least surprise: people generally expect 1/4 to equal 0.25. But the precision of the Rat is exact, rather than the approximation you’d get from a floating point number like a Num:

> say (3/7).Num + (2/7).Num + (2/7).Num - 1;
-1.11022302462516e-16
> say 3/7 + 2/7 + 2/7 - 1
0

The most straightforward way to see what is going on inside the Rat is to use the .perl method. .perl is a standard Perl 6 method which returns a human-readable string which, when eval’d, recreates the original object as closely as is possible:

> say (3/7).perl
3/7
You can also pick at the components of the Rat:
> say (3/7).numerator
3
> say (3/7).denominator
7
> say (3/7).nude.perl
[3, 7]

All the standard numeric operators and operations work on Rats. The basic arithmetic operators will generate a result which is also a Rat if that is possible; the rest will generate Nums:

> my $a = 1/60000 + 1/60000; say $a.WHAT; say $a; say $a.perl
Rat()
3.33333333333333e-05
1/30000
> my $a = 1/60000 + 1/60001; say $a.WHAT; say $a; say $a.perl
Num()
3.33330555601851e-05
3.33330555601851e-05
> my $a = cos(1/60000); say $a.WHAT; say $a; say $a.perl
Num()
0.999999999861111
0.999999999861111

(Note that the 1/60000 + 1/60000 didn’t work in the last official release of Rakudo, but is fixed in the Rakudo github repository.)
There also is a nifty method on Num which creates a Rat within a given tolerance of the Num (default is 1e-6):

> say 3.14.Rat.perl
157/50
> say pi.Rat.perl
355/113
> say pi.Rat(1e-10).perl
312689/99532

One interesting development which has not made it into the main Rakudo build yet is decimal numbers in the source are now spec’d to be Rats. Luckily this is implemented in the ng branch, so it is possible to demo how it will work once it is in mainstream Rakudo:

> say 1.75.WHAT
Rat()
> say 1.75.perl
7/4
> say 1.752.perl
219/125

One last thing: in Rakudo, the Rat class is entirely implemented in Perl 6. The source code is thus a pretty good example of how to implement a numeric class in Perl 6.

.pick your game


December 15, 2009
又一個(gè)大學(xué)學(xué)期結(jié)束了割以,或者快要結(jié)束了,對于身在美國的大多數(shù)來說应媚。這個(gè)禮物會(huì)有些樂趣严沥,他可以 .pick 東西。
.pick 允許從一個(gè)列表中選擇隨機(jī)的元素中姜,先來看看Perl5 的語法:

my @dice = (1, 2, 3, 4, 5, 6);
my $index = int (rand() * scalar @dice);
print $dice[$index] . "\n";

5

Perl 6 可以簡化這消玄,同時(shí)能選擇多個(gè)元素.

my @dice = 1..6;
say @dice.pick(2).join(" ");
> 3 4

僅僅使用一套骰子,你就可以和你的朋友們進(jìn)行角色扮演的會(huì)話了≡玻現(xiàn)在讓我們看看使用 10 次6面的骰子會(huì)有多少攻擊:

my @dice = 1..6;
say @dice.pick(10).join(" ");
> 5 3 1 4 2 6

對那些懷疑者莱找,上面的結(jié)果并非拼寫錯(cuò)誤。 .pick 的行為實(shí)際上和它的名字是一致的嗜桌。當(dāng)你把某個(gè)東西選出來奥溺,你通常不會(huì)把它放回去了。如果你想把它們再放回去骨宠,允許同一個(gè)項(xiàng)目被再次選中浮定,請?jiān)诘诙€(gè)參數(shù)中使用副詞 :repalce。

my @dice = 1..6;
say @dice.pick(10, :replace).join(" ");
> 4 1 5 6 4 3 3 5 1 1

Note to game masters: don’t invite me to your D&D games unless you need someone with terrible dice luck. ;)
There is no specific order the list items have to be in for .pick to work its magic. Take the values of monopoly money, for instance:

my @dice = <1 5 10 20 50 100 500>;
say @dice.pick(10, :replace).join(" ");
> 20 50 100 500 500 10 20 5 50 20

When dice aren’t available, a deck of cards is usually on hand. This version is very basic, but is meant to get ideas going.

use v6;
class Card
{
  has $.rank;
  has $.suit;

  multi method Str()
  {
    return $.rank ~ $.suit;
  }
}

my @deck;
for <A 2 3 4 5 6 7 8 9 T J Q K> -> $rank
{
  for <? ? ? ?> -> $suit
  {
    @deck.push(Card.new(:$rank, :$suit));
  }
}
# Shuffle the cards.
@deck .= pick(*);
say @deck.Str;
> Not outputting the results here.

What does the pick(*) do? Call that a sneak peak for another gift. For now, see if you can improve on the card code and make a deck class.
With that, I hope I have proven that Perl 6 is fun. It certainly gets a high mark from me. ?

Whatever


by Moritz

Whatever 在 Perl 6 中是一種類型层亿,在它出現(xiàn)的上下文中桦卒,Whatever 代表著它知道的任何東西。
例子:

1..*                 # infinite range
my @x = <a b c d e>;
say @x[*-2]          # indexing from the back of the array
                     # returns 'd'
say @x.map: * ~ 'A'; # concatenate A to whatever the
                     # thing is we pass to it
say @x.pick(*)       # randomly pick elements of @x
                     # until all are used up

say @array[*-5] 等價(jià)于:
say @array[-> $x { $x-5 }]; # $x 是數(shù)組元素的個(gè)數(shù)

my $make-index = -> $x { $x-5 };
say @array[$make-index];

所以這是怎么回事匿又?
有些用法看起來很明顯: *term 位置上會(huì)產(chǎn)生一個(gè) Whatever 對象方灾, 并且有些內(nèi)置函數(shù)(例如 List.pick) 知道怎么處理這個(gè) Whatever 對象。
編輯器讀取代碼后碌更, 知道怎么解析項(xiàng)和操作符:

say  2 + 4
|    | | |
|    | | + term (literal number)
|    | + operator (binary +)
|    +  term (literal number)
+ term (listop), which expects another term

所以裕偿,當(dāng)你寫下:
* * 2
編譯器會(huì)把 第一個(gè) * 解釋為 項(xiàng), 把第二個(gè) * 解釋為 操作符
上面那行代碼生成了一個(gè)代碼塊: * * 2 等價(jià)于 -> $x { $x * 2 }痛单, 你可以想任何其它子例程或 block 一樣調(diào)用它:

my $x = * * 2;
say $x(4);     # says 8

同樣地:

say @x.map: * ~ 'A';

等價(jià)于

say @x.map: -> $x { $x ~ 'A' };

say @x.map: *.succ;

等價(jià)于

say @x.map: -> $x { $x.succ };

Whatever 在排序時(shí)很有用 — 例如嘿棘, 根據(jù)數(shù)字大小排序( 前綴 '+' 意味著獲取某個(gè)東西的數(shù)字值):

@list.sort: +*

等價(jià)于:

my $desc = -> $a, $b { $a <=> $b }
@list.sort: $desc

而把列表元素作為字符串排序 (前綴 '~' 意思是獲取某個(gè)東西的字符串值):

@list.sort: ~*

Junctions


December 13, 2009
Among the many exciting things in Perl 6, junctions are one of my favourites. While I know I don’t really comprehend everything you can do with them, there are a few useful tricks which I think most people will appreciate, and it is those which I’m going to cover as today’s gift.
Junctions are values which have (potentially) more than one value at once. That sounds odd, so let’s get thinking about some code which uses them. First, let’s take an example. Suppose you want to check a variable for a match against a set of numbers:

if $var == 3 || $var == 5 || $var == 7 { ... }

I’ve never liked that kind of testing, seeing as how it requires much repetition. With an any junction we can rewrite this test:

if $var == any(3, 5, 7) { ... }

How does this work? Right near the core of Perl 6 is a concept called junctive autothreading. What this means is that, most of the time, you can pass a junction to anything expecting a single value. The code will run for each member of the junction, and the result will be all those results combined in the same kind of junction which was originally passed.
In the sample above, the infix:<==> operator is run for each element of the junction to compare them with $var. The results of each test are combined into a new any junction, which is then evaluated in Boolean context by the if statement. An any junction in Boolean context is true if any of its values are true, so if $var matches any value in the junction, the test will pass.
This can save a lot of duplicated code, and looks quite elegant. There’s another way to write it, as any junctions can also be constructed using the infix:<|> operator:

if $var == 3|5|7 { ... }

What if you want to invert this kind of test? There’s another kind of junction that’s very helpful, and it’s called none:

if $var == none(3, 5, 7) { ... }

As you may have guessed, a none junction in Boolean context is true only if none of its elements are true.
Junctive autothreading also applies in other circumstances, such as:

my $j = any(1, 2, 3);
my $k = $j + 2;

What will this do? By analogy to the first example, you can probably guess that $k will end up being any(3, 4, 5).
There is an important point to note in these examples. We’re talking about junctive autothreading, which should give you a hint. By the Perl 6 spec, the compiler is free to run these multiple operations on junctions in different threads so that they can execute in parallel. Much as with hyperoperators, you need to be aware that this could happen and avoid anything which would make a mess if run simultaneously.
The last thing I want to talk about is how junctions work with smartmatching. This is really just another instance of autothreading, but there are some other junction types which become particularly useful with smartmatching.
Say you have a text string, and you want to see if it matches all of a set of regexes:

$string ~~ /<first>/ & /<second>/ & /<third>/

Assuming, of course, you have defined regexes called first, secondand third. Rather like |, & is an infix operator which creates junctions, this time all junctions which are only true if all their members are true.
The great thing about junctions is that they have this behaviour without the routine you’re passing them to having to know about it, so you can pass junctions to almost any library or core function and expect this kind of behaviour (it is possible for a routine to deliberately notice junctions and treat them how it prefers rather than using the normal autothreading mechanism). So if you have a routine which takes a value to smartmatch something against, you can pass it a junction and get that flexibility in the smartmatch for free. We use this in the Perl 6 test suite, with functions like Test::Util::is_run, which runs some code in another interpreter and smartmatches against its output.
To finish off, here are some other useful things you can do with junctions. First, checking if $value is present in @list:

any(@list) == $value

Junction constructors can work quite happily with the elements of arrays, so this opens up many possibilities. Others include:

all(@list) > 0; # All members greater than zero?
all(@a) == any(@b); # All elements of @a present in @b?

Go experiment, and have fun!

Modules and Exporting


December 12, 2009
Today I’d like to talk about a fairly fundamental subject: libraries.
To write a library in Perl 6, we use the “module” keyword:

module Fancy::Utilities {
    sub lolgreet($who) {
        say "O HAI " ~ uc $who;
    }
}

Put that in Fancy/Utilities.pm somewhere in $PERL6LIB and we can use it like the following:

use Fancy::Utilities;
Fancy::Utilities::lolgreet('Tene');

That’s hardly ideal. Just like in Perl 5, we can indicate that some symbols from the module should be made available in the lexical scope of the code loading the module. We’ve got a rather different syntax for it, though:

# Utilities.pm
module Fancy::Utilities {
  sub lolgreet($who) is export {
    say "O HAI " ~ uc $who;
  }
}
# foo.pl
use Fancy::Utilities;
lolgreet('Jnthn');
If you don’t specify further, symbols marked “is export” are exported by default.  We can also choose to label symbols as being exported as part of a different named group:
module Fancy::Utilities {
sub lolgreet($who) is export(:lolcat, :greet) {
  say "O HAI " ~ uc $who;
}
sub nicegreet($who) is export(:greet, :DEFAULT) {
  say "Good morning, $who!"; # Always morning?
}
sub shortgreet is export(:greet) {
  say "Hi!";
}
sub lolrequest($item) is export(:lolcat) {
  say "I CAN HAZ A {uc $item}?";
}
}

Those tags can be referenced in the code loading this module to choose which symbols to import:

use Fancy::Utilities; # Just get the DEFAULTs
use Fancy::Utilities :greet, :lolcat;
use Fancy::Utilities :ALL; # Everything marked is export
Multi subs are export by default, so you only need to label them if you want to change that.
multi sub greet(Str $who) { say "Good morning, $who!" }
multi sub greet() { say "Hi!" }
multi sub greet(Lolcat $who) { say "O HAI " ~ $who.name }
Classes are just a specialization of modules, so you can export things from them as well.  In addition, you can export a method to make it available as a multi sub.  For example, the setting exports the “close” method from the IO class so you can call “close($fh);”
class IO {
    ...
    method close() is export {
        ...
    }
    ...
}

Perl 6 does support importing symbols by name from a library, but Rakudo does not yet implement it.

Roles


by jnthnwrthngtn
As the snow falls outside, we grab a glass of mulled wine – or maybe a cup of eggnog – to enjoy as we explore today’s exciting gift – roles!
Traditionally in object oriented programming, classes have taken on two tasks: instance management and re-use. Unfortunately, this can end up pulling classes in two directions: re-use wants them to be small and minimal, but if they’re representing a complex entity then they need to support all of the bits it needs. In Perl 6, classes retain the task of instance management. Re-use falls to roles.
So what does a role look like? Imagine that we are building up a bunch of classes that represent different types of product. Some of them will have various bits of data and functionality in common. For example, we may have a BatteryPower role.

role BatteryPower {
    has $.battery-type;
    has $.batteries-included;
    method find-power-accessories() {
        return ProductSearch::find($.battery-type);
    }
}

At first glance, this looks a lot like a class: it has attributes and methods. However, we can not use a role on its own. Instead, we must compose it into a class, using the does keyword.

class ElectricCar does BatteryPower {
    has $.manufacturer;
    has $.model;
}

Composition takes the attributes and methods – including generated accessors – from the role and copies them into the class. From that point on, it is as if the attributes and methods had been declared in the class itself. Unlike with inheritance, where the parents are looked at during method dispatch, with roles there is no runtime link beyond the class knowing to say “yes” if asked if it does a particular role.
Where things get really interesting is when we start to compose multiple roles into the class. Suppose that we have another role, SocketPower.

role SocketPower {
    has $.adapter-type;
    has $.min-voltage;
    has $.max-voltage;
    method find-power-accessories() {
        return ProductSearch::find($.adapter-type);
    }
}

Our laptop computer can be plugged in to the socket or battery powered, so we decide to compose in both roles.
class Laptop does BatteryPower does SocketPower {
}
We try to run this and…BOOM! Compile time fail! Unlike with inheritance and mix-ins, role composition puts all of the roles on a level playing field. If both provide a method of the same name – in this case, find-power-accessories – then the conflict will be detected as the class is being formed and you will be asked to resolve it. This can be done by supplying a method in our class that says what should be done.

class Laptop does BatteryPower does SocketPower {
    method find-power-accessories() {
        my $ss = $.adapter-type ~ ' OR ' ~ $.battery-type;
        return ProductSearch::find($ss);
    }
}

This is perhaps the most typical use of roles, but not the only one. Roles can also be taken and mixed in to an object (on a per-object basis, not a per-class basis) using the does and but operators, and if filled only with stub methods will act like interfaces in Java and C#. I won’t talk any more about those in this post, though: instead, I want to show you how roles are also Perl 6’s way of achieving generic programming, or parametric polymorphism.
Roles can also take parameters, which may be types or just values. For example, we may have a role that we apply to products that need to having a delivery cost calculated. However, we want to be able to provide alternative shipping calculation models, so we take a class that can handle the delivery calculation as a parameter to the role.

role DeliveryCalculation[::Calculator] {
    has $.mass;
    has $.dimensions;
    method calculate($destination) {
        my $calc = Calculator.new(
            :$!mass,
            :$!dimensions
        );
        return $calc.delivery-to($destination);
    }
}

Here, the ::Calculator in the square brackets after the role name indicates that we want to capture a type object and associate it with the name Calculator within the body of the role. We can then use that type object to call .new on it. Supposing we had written classes that did shipping calculations, such as ByDimension and ByMass, we could then write:

class Furniture does DeliveryCalculation[ByDimension] {
}
class HeavyWater does DeliveryCalculation[ByMass] {
}

In fact, when you declare a role with parameters, what goes in the square brackets is just a signature, and when you use a role what goes in the square brackets is just an argument list. Therefore you have the full power of Perl 6 signatures at your disposal. On top of that, roles are “multi” by default, so you can declare multiple roles with the same short name, but taking different types or numbers of parameters.
As well as being able to parametrize roles using the square bracket syntax, it is also possible to use the of keyword if each role takes just one parameter. Therefore, with these declarations:

role Cup[::Contents] { }
role Glass[::Contents] { }
class EggNog { }
class MulledWine { }

We may now write the following:

my Cup of EggNog $mug = get_eggnog();
my Glass of MulledWine $glass = get_wine();

You can even stack these up.

role Tray[::ItemType] { }
my Tray of Glass of MulledWine $valuable;

The last of these is just a more readable way of saying Tray[Glass[MulledWine]]. Cheers!
About these ads
Like this:

Like Loading...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市旭绒,隨后出現(xiàn)的幾起案子鸟妙,更是在濱河造成了極大的恐慌焦人,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件重父,死亡現(xiàn)場離奇詭異花椭,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)房午,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門个从,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人歪沃,你說我怎么就攤上這事∠铀桑” “怎么了沪曙?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長萎羔。 經(jīng)常有香客問我液走,道長,這世上最難降的妖魔是什么贾陷? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任缘眶,我火速辦了婚禮,結(jié)果婚禮上髓废,老公的妹妹穿的比我還像新娘巷懈。我一直安慰自己,他們只是感情好慌洪,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布顶燕。 她就那樣靜靜地躺著,像睡著了一般冈爹。 火紅的嫁衣襯著肌膚如雪涌攻。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天频伤,我揣著相機(jī)與錄音恳谎,去河邊找鬼。 笑死憋肖,一個(gè)胖子當(dāng)著我的面吹牛因痛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瞬哼,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼婚肆,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了坐慰?” 一聲冷哼從身側(cè)響起较性,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤用僧,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后赞咙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體责循,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年攀操,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了院仿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡速和,死狀恐怖歹垫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情颠放,我是刑警寧澤排惨,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站碰凶,受9級(jí)特大地震影響暮芭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜欲低,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一辕宏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧砾莱,春花似錦瑞筐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至扫步,卻和暖如春魔策,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背河胎。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工闯袒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人游岳。 一個(gè)月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓政敢,卻偏偏與公主長得像,于是被迫代替她去往敵國和親胚迫。 傳聞我的和親對象是個(gè)殘疾皇子喷户,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容

  • "Unterminated string literal.": "未終止的字符串文本。", "Identifier...
    兩個(gè)心閱讀 8,323評論 0 4
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理访锻,服務(wù)發(fā)現(xiàn)褪尝,斷路器闹获,智...
    卡卡羅2017閱讀 134,601評論 18 139
  • 看電影與浪漫晚餐太過普遍 送花送巧克力也已過時(shí) 或者,可以帶著你的親密戀人 給她一個(gè)羽毛球拼接的愛心驚喜 在彼此的...
    羽你同樂閱讀 179評論 0 2
  • 10月20日河哑,是我生活在這個(gè)地球上的第一萬三千一百五十天避诽,也是我工作的第十個(gè)年頭,因此璃谨,在...
    曹鑫x閱讀 375評論 0 0
  • 熟人隔閡沙庐, 成為陌路。 陌路交集佳吞, 演繹熟人拱雏? 殊不知,一心別底扳。 心是人體自帶調(diào)頻器古涧, 有的人鐘愛一個(gè)頻一首曲 有...
    唯一芝現(xiàn)閱讀 434評論 0 1