免费注册 查看新帖 |

Chinaunix

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

请教大骆驼一个示例 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2012-12-01 16:07 |只看该作者 |倒序浏览
本帖最后由 ljpdxj 于 2012-12-01 17:34 编辑

如下:
  1. sub try (&$) {
  2.     my ($try, $catch) = @_;
  3.     eval { &$try };
  4.     if ($@) {
  5.         local $_ = $@;
  6.         &$catch;
  7.     }
  8. }
  9. sub catch (&) { $_[0] }

  10. try {
  11.     die "phooey";
  12. }                   # not the end of the function call!
  13. catch {
  14.     /phooey/ and print "unphooey\n";
  15. };
复制代码
哎,大骆驼确实太伤脑细胞了。。。
看到上面的示例时,晕在如下行到底要如何理解
sub catch (&) { $_[0] }
还有字符串 $@是如何传递给catch匿名函数的呢?
请大大指示下,十分感谢!!!
函数引用的传递和解引用以及$@都能理解
自问自答:
调用Try时首先解析其参数,第二个参数其实就是一个指向匿名函数
{
    /phooey/ and print "unphooey\n";
};
的引用

在Try中调用此引用,先判断$_是否匹配字符串,若匹配就打印in phooey

论坛徽章:
0
2 [报告]
发表于 2012-12-01 16:12 |只看该作者
本帖最后由 ljpdxj 于 2012-12-01 17:39 编辑

顺便问下为何代码段的注释使用=和=cut,为何把下面的所有代码都注释了?具体是啥情况?
自问自答:
原来是开头的等号必须在行的开头并后面紧跟着注释的内容。。。


论坛徽章:
0
3 [报告]
发表于 2012-12-01 16:53 来自手机 |只看该作者
找到分析,先帖上来自己读
The try/catch example in the Prototype section of Programming Perl often causes confusion among programmers who are trying to understand prototypes.
sub try (&$) {
   my($try, $catch) = @_;
   eval { $try };
   if ($@) {
      local $_ = $@;
      &$catch;
   }
}

sub catch (&) { $_[0] }
[download]
This is invoked like this:
  try {
     die "phooey";
  }
  catch {
     /phooey/ and print "unphhoey\n";
  }
[download]
The usual questions at this point are "WHAT?" and "How in the heck does that even work?" So this is an explication in detail. Let's summarize how subroutine calls work first, and then tackle the prototype stuff after.
Remember that when Perl calls a subroutine, it evaluates any expressions supplied as arguments first and then passes the values of those expressions as the arguments to the subroutine. So in this case, try() wants two expressions as its arguments: one is required to evaluate to a reference to a subroutine, and the other is required to evaluate to a scalar. On the other hand, catch() requires only one: some thing that evaluates to a subroutine reference.
Remember that an anonymous sub is actually an expression; it has the side effect of generating code that can be executed later, but it's actually just an expression that returns a scalar value: the reference that points to the generated code.

So how do the try() and catch() subs work, and why does the syntax shown actually compile?

Let's look at catch() first. It's just an identity function:

  sub identity {
      return $_[0];
  }
[download]
This returns whatever the first argument evaluated to as the value of the subroutine. This is exactly what catch() does, though it states it slightly more compactly, and uses a (&) prototype to constrain its argument to be a subroutine reference. So why is this useful, and why does it use an & prototype?
The & prototype, when it comes first, does two things:

It allows you to pass an anonymous sub without the sub keyword in front
it allows you to leave off the comma following this first argument
So catch()'s prototype says "Give me a sub reference somehow. An anonymous sub without the sub keyword will be fine, and I require that you give me no other arguments." So a catch() call could be any of the following:
   sub example {
      /foo/ and print "foo detected";
   }
   
   catch \&example;
   
   catch sub {
               /foo/ and print "foo detected";
             };
            
   catch {
           /foo/ and print "foo detected";
   }
[download]
This last one is the one we'll be using for our example.
Now remember that catch() just returns its first argument as its value. All of these argument forms shown above evaluate to the same kind of thing: a reference to a subroutine. A reference to a subroutine is always a scalar, so a call to catch will be acceptable to try() as its second parameter (the prototype being &$: a sub reference, and a scalar).

So now we know we've got something that works for the second argument of try(). Let's look at the first one. This is &, so it can accept the same kinds of things that catch() did as its first parameter: an actual reference to a sub (like \&example), an anonymous sub ( sub { ... }), or a "headless" anonymous sub (like { ... }).

Remember that the second option you have when using & as the first argument prototype lets you leave off the comma as well as the sub, so the total call looks like this:

  try {
    # some code that dies
  }
  catch {
    # pattern match against $_ for die string
  }
[download]
Which is really, if you wrote it all out in the standard sub call syntax:
  try( sub{
            # code that dies
       },
       catch(sub {
                    # code that checks vs. $_
                 }
         )
  );
[download]
So. To call try(), Perl will have to evaluate any expressions in its argument list and then pass them in.
The first argument, when evaluated, yields a reference to some code to be called. This is the first (headless ) anonymous sub, with no trailing comma.

The second argumentis a call to catch() that has to be evaluated. The catch() call, as mentioned before, simply returns its argument to the caller. Its argument is an anonymous sub, so the returned value is a reference to this (unevaluated) sub. This is a scalar, so it matches the $ in the &$ prototype.

Now we have the arguments to try(): the expected subroutine reference (the & in &$) from the code block right after the try() sub name, and the reference to the anonymous sub returned by catch() (the $ in &$). We're ready to call try() at this point.

Inside try(), we eval the first anonymous sub. Since it's inside an eval block, the code does whatever it does and (possibly) sets $@ if it dies without further disturbing the flow of control. If $@ is set after the eval, we localize $_ (this creates a new global variable that we'll reference via $_ during this block) and set it to $@. Then we just call the second anonymous sub, which expects to see the contents of $@ in $_, where we've conveniently just put them. Since this is inside the block that localized $_, we sill get the "new" $_. This code does whatever it wants to do, then returns. The block ends, and $_ is restored to whatever value it had previous to the if ($@) block, and try() exits.

So the concepts here that are important are:

subroutines evaluate their arguments before they are called.
sub { $_[0] } returns its first argument's value as its value when it is called.
sub {} is an expression, returning a reference to an nonymous sub while compiling the code at the point where the sub block is evaluated
local() defines a new copy of a variable, and blocks called by this one get the new copy when referring to the variable. When the block localizing the variable ends, the old variable is restored.
a leading & in a prototype lets you leave off the "sub" in an anonymous sub definition, and lets you leave off the comma following the block too.
Edited 8/04: errant "*" removed in try() prototype
Edited 8/09: Typos corrected

论坛徽章:
8
双子座
日期:2013-08-31 07:37:12金牛座
日期:2013-09-09 18:49:12处女座
日期:2013-09-23 11:43:14处女座
日期:2013-10-09 19:48:21狮子座
日期:2014-03-24 18:22:12丑牛
日期:2014-04-22 22:07:51申猴
日期:2014-06-12 21:54:13双鱼座
日期:2014-06-13 21:52:31
4 [报告]
发表于 2012-12-01 18:28 |只看该作者
小骆驼刚看完

论坛徽章:
0
5 [报告]
发表于 2012-12-01 18:42 |只看该作者
大骆驼中的原型函数,我就看了一遍,没记住多少,具体分析的时候还的翻翻书。
btw:中文版的大骆驼好多错误啊&
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP