- 论坛徽章:
- 1
|
本帖最后由 iakuf 于 2014-05-12 16:00 编辑
"Perl 5 to 6" Lesson 04 - 子函数和参数
概要- # Perl 5 中传参的方式
- sub print_arguments {
- say "Arguments:";
- for @_ {
- say "\t$_";
- }
- }
- # 参数在这个地方使用固定的位置和指定的类型:
- sub distance(Int $x1, Int $y1, Int $x2, Int $y2) {
- return sqrt ($x2-$x1)**2 + ($y2-$y1)**2;
- }
- say distance(3, 5, 0, 1);
- # 默认参数
- sub logarithm($num, $base = 2.7183) {
- return log($num) / log($base)
- }
- say logarithm(4); # 这会给第二个参数使用默认的值
- say logarithm(4, 2); # 明确的指名第二个参数
- # 命名参数
- sub doit(:$when, :$what) {
- say "doing $what at $when";
- }
- doit(what => 'stuff', when => 'once'); # 'doing stuff at once'
- doit(:when<noon>, :what('more stuff')); # 'doing more stuff at noon'
- # illegal: doit("stuff", "now")
复制代码 描述
子函数是使用 sub 关键字来声明, 并且有形式参数列表, 就象 C, Java 和一些其它的语言. 这些参数有可选的类型约束.
参数默认是只读的. 可通过 "traits" 来修改成可以读写.- sub try-to-reset($bar) {
- $bar = 2; # 禁止修改参数
- }
- my $x = 2;
- sub reset($bar is rw) {
- $bar = 0; # 可以修改
- }
- reset($x); say $x; # 0
- sub quox($bar is copy){
- $bar = 3;
- }
- quox($x); say $x # still 0
复制代码 参数可以在后面增加一个问号来标记是否可选.- sub foo($x, $y?) {
- if $y.defined {
- say "Second parameter was supplied and defined";
- }
- }
- sub bar($x, $y = 2 * $x) {
- ...
- }
复制代码 命名参数
当你象这样调用子函数: my_sub($first, $second). 在这的这个 $first 参数会绑定到第一个正式的参数, $second 会传递给第二个参数..等等, 这就是所谓的位置 "positional".
有时, 我们希望使用更加容易记住的名字, 而不是数字之类. 这是为什么使用命名参数的原因:- my $r = Rectangle.new(
- x => 100,
- y => 200,
- height => 23,
- width => 42,
- color => 'black'
- );
复制代码 当你看到这样这些名字, 你马上知道参数具体是什么含义。
要定义一个命名参数, 你只需要给 : 放在参数之前:- sub area(:$width, :$height) {
- return $width * $height;
- }
- area(width => 2, height => 3);
- area(height => 3, width => 2 ); # 同上
- area(:height(3), :width(2)); # 同上
复制代码 这最后一个例子使用的是叫 colon pair syntax 的语法. 当给使用 :draw-perimeter 给其赋布尔值的时候, 你什么都不写默认是 True, 当想给一个参数赋值为布尔类型的 FALSE 的时候,也不需要写明 0,而是用以下格式:!transparent
- :draw-perimeter # same as "draw-perimeter => True"
- :!transparent # same as "transparent => False"
复制代码 在命名参数的声明, 变量也被用来作为参数. 您可以使用不同的名称:- sub area(:width($w), :height($h)) {
- return $w * $h;
- }
- area(width => 2, height => 3);
复制代码 Slurpy 参数
你给你的 sub 参数的时候, 有时你并不知道所有的参数个数. 你可以定义一种称作 slurpy 参数来提交任何所剩余的参数:- sub tail ($first, *@rest){
- say "First: $first";
- say "Rest: @rest[]";
- }
- tail(1, 2, 3, 4); # "First: 1\nRest: 2 3 4\n"
复制代码 对于哈希, 命名的 slurpy 参数定义是使用有 * 号前缀的哈希参数:- sub order-meal($name, *%extras) {
- say "I'd like some $name, but with a few modifications:";
- say %extras.keys.join(', ');
- }
- order-meal('beef steak', :vegetarian, :well-done);
复制代码 Interpolation
默认数组是不能插入到参数的列表中. 所以这不同于 Perl 5 的写法.- sub a($scalar1, @list, $scalar2) {
- say $scalar2;
- }
- my @list = "foo", "bar";
- a(1, @list, 2); # 2
复制代码 这也意味着, 默认的情况下你不能使用一个列表做为参数列表:- my @indexes = 1, 4;
- say "abc".substr(@indexes) # 不能实现你想做的
复制代码 ( 实际的情况是, 这的第一个参数需要是一个 Int, 需要被强制转换成 Int, 所以你可以写成 "abc."substr(@indexes.elems) 来放在第一位 ).
当然, 你也可以使用前缀 | 来完成所需的行为- say "abcdefgh".substr(|@indexes) # 结果 bcde, 等同于 "abcdefgh".substr(1, 4)
复制代码 Multi Subs
实际上, 你其实可以定义多个 sub 来使用相同的名字, 但接收不同的参数列表:- multi sub my_substr($str) { ... } # 1
- multi sub my_substr($str, $start) { ... } # 2
- multi sub my_substr($str, $start, $end) { ... } # 3
- multi sub my_substr($str, $start, $end, $subst) { ... } # 4
复制代码 现在, 当你调用这个 sub 时, 所述的参数会有一个匹配上并选择使用的.
这个 multis 并不只是可以工作在参数数量不等的情况上, 也可以用于在不同的参数类型的时候:- multi sub frob(Str $s) { say "Frobbing String $s" }
- multi sub frob(Int $i) { say "Frobbing Integer $i" }
- frob("x"); # Frobbing String x
- frob(2); # Frobbing Integer 2
复制代码 动机
没有人会怀疑明确的子函数参数的实用性: 少打字, 少重复的参数检查, 更多的安全文档代码.
这些功能还有益于自省. 例如, 当传递给一个块或者子函数使用 Array.sort, 并且这段代码预期只有一个参数, 完成 Schwartzian 转换( 见 http://en.wikipedia.org/wiki/Schwartzian_transform ) 是自动的 - 这些功能在 Perl 5 中不太可能, 由于少了明确的参数, 意味着排序代码块永远不知道有多少个参数.
Multi subs 这个功能也非常有用, 因为它可以使用新类型来覆盖内建命令. 比如, 你想有一个版本的 Perl6 是本地化上来正确处理土耳其字符串,这对大小写转换是使用的不同寻常的规则时非常好用.
这时, 不需要修改语言本身. 你只需要加入一个新的 TurkishStr, 并为原生的函数加入一个 multi subs:- multi uc(TurkishStr $s) { ... }
复制代码 现在, 所有这个对字符串处理就会自动对应他们的语言类型, 然后你就可以使用 uc 函数象内置的函数一样.
由于操作也可以使用 subs, 所以这些改进操作是可以正常工作的.
另请参阅
http://perlcabal.org/syn/S06.html |
|