免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2606 | 回复: 3
打印 上一主题 下一主题

perlsub 中 prototype 部分的翻译 [复制链接]

论坛徽章:
46
15-16赛季CBA联赛之四川
日期:2018-03-27 11:59:132015年亚洲杯之沙特阿拉伯
日期:2015-04-11 17:31:45天蝎座
日期:2015-03-25 16:56:49双鱼座
日期:2015-03-25 16:56:30摩羯座
日期:2015-03-25 16:56:09巳蛇
日期:2015-03-25 16:55:30卯兔
日期:2015-03-25 16:54:29子鼠
日期:2015-03-25 16:53:59申猴
日期:2015-03-25 16:53:29寅虎
日期:2015-03-25 16:52:29羊年新春福章
日期:2015-03-25 16:51:212015亚冠之布里斯班狮吼
日期:2015-07-13 10:44:56
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-11-27 22:45 |只看该作者 |倒序浏览
翻译了 5.14 中关于 prototype 部分的内容

=head1 prototype

Perl 可以通过函数元型在编译期进行有限的参数类型检验。如果你声明

    sub mypush (+@)

那么 mypush() 对参数的处理就同内置的 push() 完全一样了。函数声明必须要在编译
相应函数调用之前告知编译器(编译器在编译函数调用时会对相应函数用 prototype
来查询它的元型来进行参数检验,并决定怎样编译此函数调用)。元型只在不用 & 调用
函数的时候起作用。就是说在语法上如果你想像内置函数一样调用,它就表现的像
内置函数一样。如果想用过时的风格通过 & 调用,那么编译器就无视函数声明。另外
元型在函数引用如 \&foo 和间接调用如 &{$subref} 和 $subref->() 时也不起作用。

方法调用也不受元型影响,因为实际调用的函数无法在编译期决定,它是由继承关系
决定的。

因为这个特性最初的目的是使你可以像内置函数那样调用自己的函数,所以下面就给出
等价于内置函数调用方式的函数元型。

    声明为                  调用方式

    sub mylink ($$)         mylink $old, $new
    sub myvec ($$$)         myvec $var, $offset, 1
    sub myindex ($$;$)      myindex &getstring, "substr"
    sub mysyswrite ($$$;$)  mysyswrite $buf, 0, length($buf) - $off, $off
    sub myreverse (@)       myreverse $a, $b, $c
    sub myjoin ($@)         myjoin ':', $a, $b, $c
    sub mypop (+)           mypop @array
    sub mysplice (+$$@)     mysplice @array, 0, 2, @pushme
    sub mykeys (+)                mykeys %{$hashref}
    sub myopen (*;$)            myopen HANDLE, $name
    sub mypipe (**)                mypipe READHANDLE, WRITEHANDLE
    sub mygrep (&@)                mygrep { /foo/ } $a, $b, $c
    sub myrand (;$)                myrand 42
    sub mytime ()                mytime

任何 \ 跟着的函数元型中的字符代表着实际的参数必须由相应字符开头(参数前可跟
my our local 声明),只有 $ 例外,它可以接收并不以 $ 开头的 hash 和数组的元素,
比如 my_function()->[0]。传给 @_ 的参数将会是相应实际参数的引用,即对它加 \。

你可以用 \[] 来表示多个可用的类型。比如:

    sub myref (\[$@%&*])

上面的函数声明允许像下面这样调用 myref() 这个函数

    myref $var
    myref @array
    myref %hash
    myref &sub
    myref *glob

传入函数 myref 的第一个参数将分别是一个 scalar、数组、hash、函数、glob 的引用。

函数元型中前面不跟 \ 的字符有特殊意义。任何不跟 \ 的 @ % 将代表剩下的所有参数,
并提供 list context。而 $ 将提供 scalar context。 & 表示需要一个匿名函数(即
sub { } 这样的结构,不能是变量),当用作第一个参数时可以省掉 sub 关键字(如果
省掉 sub 则后面跟的逗号也必须要省掉).

* 表明可以接收一个 bareword、常量、scalar 表达式、typeglob或 typeglob 的引用。
传入函数的参数要么是一个简单的 scalar 要么是 typeglob 的引用(后两种情况)。如果
你总是想要一个 typeglob 的引用可以用 Symbol::qualify_to_ref() 将名字转换成相应
的 typeglob 的引用:

    use symbol 'qualify_to_ref';

    sub foo (*) {
        my $fh = qualify_to_ref(shift, caller);
        ...
    }

+ 类似于 $ 但是当遇到数组变量或 hash 变量时表示 \[@%],在其它情况下总是提供
scalar context。它适用于可以接收数组变量或数组引用为参数的函数:

    sub mypush (+@) { # 5.14 中 push 第一个参数可以为数组的引用
        my $aref = shift;
        die "Not an arrayref" unless ref $aref eq 'ARRAY';
        push @$aref, @_;
    }

当用 + 时函数必须要检验实际的参数是否是自己需要的类型,因为它不区分 @ %。

分号 ; 用来分隔必须的参数和可选的参数。它必须在 @ % 之前,因为它们代表剩下的
所有参数。

在元型最后或在 ; 之前可以用 _ 来代替 $:它表示如果没有提供这个参数会传递 $_
作为对应的参数,它可以用来实现默认参数的语法。

注意上面列表最后3个例子,mygrep() 表现的就像列表操作符,myrand() 表现的就像
rand() 一样为一元操作符,mytime() 就像 time() 一样完全不需要参数。如果你这么用:

    mytime + 2;

你将会得到 mytime() + 2,而不是 mytime(2),没有函数元型根本无法实现这样的效果。

有意思的是你可以把 & 用在最开始的位置来创造新语法:

    sub try (&@) {
        my ($try, $catch) = @_;
        eval { &$try };
        if ($@) {
            local $_ = $@;
            &$catch;
        }
    }

    sub catch (&) { $_[0] }

    try {
        die "phooey";
    } catch {
        /phooey/ and print "unphooey\n";
    };

上面的代码会打印 "unphooey",即是 Try::Tiny 的实现方法。(当然用 &$catch 会
将 @_ 暴露给 $catch 但这里并不是我们要考虑的)。

让我们重新实现下 Perl 的 grep 操作符:

    sub mygrep (&@) { # 无法实现 grep EXPR,LIST 这个语法
        my $code = shift;
        my @result;
        foreach $_ (@_) {
            push @result, $_ if &$code;
        }
        @result;
    }

请不在要函数元型中使用字母或数字,它们被保留作它用,或许在将来用于实现完整的
参数列表。不要为老的代码添加上函数元型,因为有时会改变语意出来奇怪的结果。比如:

    sub func ($) {
        my $n = shift;
        print "you ave me $n\n";
    }

某人在代码中这么调用它:

    func(@foo);
    func(split /:/);

只是声明了函数 func 只接收一个 scalar 参数却带来了灾难性的结果,原来参数所处的
list context 被改为 scalar context,传入的参数变成 @foo 的元素个数,和分割的
元素个数。

元型很强大也很危险,请小心慎用。

论坛徽章:
46
15-16赛季CBA联赛之四川
日期:2018-03-27 11:59:132015年亚洲杯之沙特阿拉伯
日期:2015-04-11 17:31:45天蝎座
日期:2015-03-25 16:56:49双鱼座
日期:2015-03-25 16:56:30摩羯座
日期:2015-03-25 16:56:09巳蛇
日期:2015-03-25 16:55:30卯兔
日期:2015-03-25 16:54:29子鼠
日期:2015-03-25 16:53:59申猴
日期:2015-03-25 16:53:29寅虎
日期:2015-03-25 16:52:29羊年新春福章
日期:2015-03-25 16:51:212015亚冠之布里斯班狮吼
日期:2015-07-13 10:44:56
2 [报告]
发表于 2011-11-27 22:56 |只看该作者
函数元型用作语法糖会带来很惊艳的效果不过和 OO 无缘所以比较少见。 prototype 函数的文档中也说明了内置函数的实现方法和 prototype 完全不同,所以不要期望用元型来模拟任何内置函数。

因为元型完全是在代码编译阶段进行的所以有着各种限制,在文中也插入了几句来强调这也是理解的难点。

论坛徽章:
0
3 [报告]
发表于 2011-11-28 11:11 |只看该作者
做个记号,慢慢看

论坛徽章:
0
4 [报告]
发表于 2012-04-17 09:48 |只看该作者
很详细,感谢!
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP