这两天决定试一把 perl6,因为扶凯兄已经把还没有正式发行 rakudo star 包的 moarvm 编译打包好了,所以可以跳过这步直接进入模块安装。当然,源码编译本身也没有太大难度,只不过从 github 下源码本身耗时间比较久而已。
既然木有 star 包,那么安装好 moarvm 上的 rakudo 后我们就有必要先自己把 panda 之类的工具编译出来。这一步需要注意一下你的 @*inc 路径和实际的 $perl6lib 路径,已经编译之后的 panda 存在的 $path 是不是都正确,如果不对的修改一下 ~/.bashrc 就好了。
我的尝试迁移对象是一个很简单的 puppet 的 enc 脚本,只涉及 sqlite 的读取,以及 yaml 格式的输出。通过 panda install dbiish 命令即可安装好 dbiish 模块。
脚本本身修改起来难度不大,结果如下:
#!/usr/bin/env perl6 use v6; use dbiish; use yaml; my $base_dir = /etc/puppet/webui; # 函数在 perl6 中依然使用 sub 关键字定义,不过有个超酷的特性是 multi sub # 脚本中没有用到,但是在 yaml::dumper 中遍地都是,这里也提一句。 # main 函数在 perl6 里可以直接用 :$opt 命令参数起 getopt 的作用 # 不过 enc 脚本就是直接传一个主机名,用不上这个超酷的特性 sub main($node) { # connect 方法接收参数选项是 |%opts,所以可以把哈希直接平铺写 # 这个 | 的用法一个月前在《using perl6》里看到过 my $dbh = dbiish.connect( 'sqlite', database => {$base_dir}/node_info.db ); my $sth = $dbh.prepare(select * from node_info where node_fqdn = ?); $sth.execute($node); my $ret = $sth.fetchrow_hashref; my $res; if ( !$ret ) { $res = { # perl5 的 qw() 在 perl6 里直接写成 。也不用再通过 [] 来指明是引用 classes => , environment => 'testing', }; } else { $res = { environment => $ret{'environment'}, parameters => { role => $ret{'role'} }, classes => {}, }; # 这个 for 的用法,在 perl5 的 text::xslate 模板里就用过 for split(',', $ret{'classes'}) -> $class { if ( $class eq 'nginx' ) { # 这个 写这行 # 如果不习惯这种流向操作符的,可以用,号,反正不能跟 perl5 那样啥都不写 # 这里比较怪的一点是我试图把这么长的一句分成多行写,包括每行后面加,我看到 yaml 代码里就用分行了,但是我这就会报错 # perl6 的正则变化较大,这里 /^#/ 要写成 /^'#'/ 或者 /^x23/ # 正则 // 前面不加 m// 不会立刻开始匹配 # 原先的 s///g 可以写作 s:g///,也可以写作对象式的 .subst(m//, '', :g),. 前面为空就是默认的 $_ # 捕获的数据存在 @() 数组里,也可以用 $/[i] 的形式获取 # 字符串内插时,不再写作 ${*},而是 {$*} 的形式 # 命名捕获这里没用上,写个示例: # $str ~~ /^(w+?)$=(w ** 4)w$/; # $/.chomp.say; # 注意里面的 w{4} 变成了 w ** 4 my @needs <== map { .subst(m/^(.+):(d+)$/, {$/[0]} max_fails=30 weight={$/[1]}, :g) } <== grep { !m/^x23/ } $a { my $name = $a.name.substr(2); - my $value = pir::getattribute__pps($node, $a.name); #rakudo + #my $value = pir::getattribute__pps($node, $a.name); #rakudo + my $value = $a.get_value($node); #for non-parrot $repr{$name} = $value; } $.dump_node($repr);
这里的 $.seen 和 $!seen 是不是晕掉了?其实 $.seen 就相当于先声明了 $!seen 后再自动创建一个 method seen() { return $!seen }。
另一处是 pir::getattribute__pps() 函数,pir 是 parrot 上的语言,而 moarvm 和 jvm 上都是先实现了一个 nqp 再用 nqp 写 perl6,不巧的是这个 pir 里的 getattribute__pps() 刚好至今还没有对应的 nqp 方法。(在 pir2nqp.todo 文件里可见)
所以只能用高级的 perl6 语言来做了。
总的来说,这个 yaml-pm6 代码里很多地方都是试来试去,同样的效果不同的写法,又比如 .which 和 .what.perl 也是混用。 而且我随手测试了一下,即使在 parrot 上,用 pir::getattribute__pps 的速度也比 attribute.get_value 还差点点。
最后提一句,目前 enc 脚本在 perl5、perl6-m、perl6-p、perl6-j 上的运行时间大概分别是 0.13、1.5、2.8、12s。moarvm 还差 perl5 十倍,领先 parrot 一倍。不过 jvm 本身启动时间很长,这里不好因为一个短时间脚本说它太慢。
另外还试了一下如果把我修改过的 yaml::dumper 类直接写在脚本里运行,也就是不编译成 moarvm 模块,时间大概是 2.5s,比 parrot 模块还快点点。
不过如何把 perl6 脚本本身编译成 moarvm 的 bytecode 格式运行还没有研究出来,直接 perl6-m --target=mbc --output=name.moarvm name.pl6 得到的文件运行 moar name.moarvm 的结果运行会内存报错。
本文地址:
转载随意,但请附上文章地址:-)