编程时多多思考吧,别光掌握perl的古怪用法而不理解其精髓!

grep map sort 的第一个参数就是回调函数,不是什么闭包,也不需要回避这个问题。


QUOTE:
原帖由 flw 于 2007-8-27 20:45 发表
grep map sort 的第一个参数就是回调函数,不是什么闭包,也不需要回避这个问题。

看阁下如此确定,区区只好又去perldoc了。。。



<<<<<<<<<<<<<<<<
    grep BLOCK LIST
    grep EXPR,LIST
            This is similar in spirit to, but not the same as, grep(1) and
            its relatives. In particular, it is not limited to using regular
            expressions.

            Evaluates the BLOCK or EXPR for each element of LIST (locally
            setting $_ to each element) and returns the list value
            consisting of those elements for which the expression evaluated
            to true. In scalar context, returns the number of times the
            expression was true.
<<<<<<<<<<<<<<<<<

无论怎么看这段话,BLOCK和EXPR都不是用来当函数调用的,而是在当前语境下进行"Evaluates",所以区区觉得
这里的BLOCK不是函数,而是一个反复求值(回调)的执行体(函数)。当闭包理解更好一点。
需要么?


QUOTE:
原帖由 KevinLee39 于 2007-8-27 20:43 发表
同意楼上2位的说法.

不知道楼主理解了多少精髓?

据说现在几个台湾朋友成了perl6的开发主力, 中国人还是第一次走到这么前沿的位置吧?
现在也正是用人的时候, 要不楼主也给我们大陆人争点光啊, 不要光在这 ...

Larry带着一坨perl大牛在定义下一代的perl————他不是一个人在战斗。


QUOTE:
原帖由 KevinLee39 于 2007-8-27 20:43 发表
同意楼上2位的说法.

不知道楼主理解了多少精髓?

据说现在几个台湾朋友成了perl6的开发主力, 中国人还是第一次走到这么前沿的位置吧?
现在也正是用人的时候, 要不楼主也给我们大陆人争点光啊, 不要光在这 ...

Tang做的主要是用Haskell编写的Pugs来完成Perl6的一个实现……好像没有参入Parrot的开发……不过都是一个大Team,区别是里面的不同小team吧。
晕,我觉得我回帖已经说得很清楚了。怎么还在纠缠?

closure 可以用来做callback,但是与一般callback不同,因为closure是保留当时context的一种匿名历程引用。所以它可以“智能”的区分是谁在调用它(因为它保留了当时的环境)。

grep 的第一个参数是callback,只是是使用了闭包的callback.
同意flw观点
似乎硝烟已过,但迷者自迷,还自以为得矣,真是……,没什么可笑的,毕竟大家都是为了真理
下面是摘自Advanced Perl Programming, 2nd Edition
By Simon Cozens
……
Closures
A closure is a code block that captures the environment where it's definedspecifically, any lexical variables the block uses that were defined in an outer scope. The following example delimits a lexical scope, defines a lexical variable $seq within the scope, then defines a subroutine sequence that uses the lexical variable.

    {
       my $seq = 3;
       sub sequence { $seq += 3 }
    }

    print $seq; # out of scope

    print sequence; # prints 6
    print sequence; # prints 9



Printing $seq after the block doesn't work, because the lexical variable is out of scope (it'll give you an error under use strict. However, the sequence subroutine can still access the variable to increment and return its value, because the closure { $seq += 3 } captured the lexical variable $seq.


我想红色高亮部分已经澄清了疑虑,如果还有什么疑惑,See perlfaq7 and perlref for more details on closures.
但愿都是为了真理!!!!!!!!!!!!!
但是grep map 并不会出现这种情况呀
so ……


QUOTE:
原帖由 cobrawgl 于 2007-8-27 19:28 发表
我学习perl是为了用perl来解决问题

而不是成为一个perl语言专家

如果面向过程的方法能解决我的问题,连OOP都懒得学

同意,这才是 perl 的核心思想之一 。 让无聊的人们去争论吧,浪费的时间是他们的
map/grep/sort 的参数都是 callback,不是 closure。
说他们是 closure 而不是 callback 的,
要么不懂 Perl,要么连什么是 closure、什么是 callback 都没分清。

先说说 sort:
pp_sort.c 里有一段:

[Copy to clipboard] [ - ]
CODE:
/*
=for apidoc sortsv_flags

Sort an array, with various options.

=cut
*/
void
Perl_sortsv_flags(pTHX_ SV **array, size_t nmemb, SVCOMPARE_t cmp, U32 flags)
{
    if (flags & SORTf_QSORT)
        S_qsortsv(aTHX_ array, nmemb, cmp, flags);
    else
        S_mergesortsv(aTHX_ array, nmemb, cmp, flags);
}

这里就是 sort 的内核实现。
其中调用的 S_qsortsv 和 S_mergesortsv 都是 C 函数,传递给他们的参数 cmp 就是 Perl 空间的那个参数。
sort 运算符的实现代码(限于篇幅,仅框架)如下:

[Copy to clipboard] [ - ]
CODE:
PP(pp_sort)
{
    dVAR; dSP; dMARK; dORIGMARK;
    .... 定义了一大堆变量
    void (*sortsvp)(pTHX_ SV **array, size_t nmemb, SVCOMPARE_t cmp, U32 flags)
      = Perl_sortsv_flags;  <---- 注意这里,将上面那个函数赋值给 sortsvp 这个指针了。
    .....
    if (PL_sortcop) { <---- 如果提供了 sort 的方法的话
        ....
        sortsvp(aTHX_ start, max,
        (is_xsub ? S_sortcv_xsub : hasargs ? S_sortcv_stacked : S_sortcv),
        sort_flags);
    }
    else{       <----- 如果没有提供的话
        ....
        sortsvp(aTHX_ start, max,
        (priv & OPpSORT_NUMERIC)
            ? ( ( ( priv & OPpSORT_INTEGER) || all_SIVs)
                ? ( overloading ? S_amagic_i_ncmp : S_sv_i_ncmp)
                : ( overloading ? S_amagic_ncmp : S_sv_ncmp ) )
            : ( IN_LOCALE_RUNTIME
                ? ( overloading
                    ? (SVCOMPARE_t)S_amagic_cmp_locale
                    : (SVCOMPARE_t)sv_cmp_locale_static)
                : ( overloading ? (SVCOMPARE_t)S_amagic_cmp : (SVCOMPARE_t)sv_cmp_static)),
        sort_flags);
    }

全都是典型的 callback 应用。
堆栈是 sortsvp 修改的,而不是 sort 的参数修改的,因此不能算 closure。
map 和 grep 没有 sort 那么复杂,看起来容易多了:

[Copy to clipboard] [ - ]
CODE:
flw@debian:~/perl-5.9.5$ cat ttt.pl
@b=(70,80,90);
@a = map { $_/10 } @b;
#@a = grep { $_==80 } @b;
#@a = sort sub { $b<=>$a }, @b;
flw@debian:~/perl-5.9.5$ ./perl -Dtls ttt.pl
(ttt.pl:0)      ENTER scope 2 at perly.c:351
(ttt.pl:2)      ENTER scope 3 at op.c:7795
(ttt.pl:2)      LEAVE scope 3 at op.c:8390
(ttt.pl:4)      ENTER scope 3 at op.c:7795
(ttt.pl:2)      LEAVE scope 3 at op.c:8390
(ttt.pl:4)      LEAVE scope 2 at perly.c:680
(ttt.pl:0)      LEAVE scope 1 at perl.c:2304
(ttt.pl:0)      ENTER scope 1 at perl.c:2312
(ttt.pl:0)      popping jumplevel was bfc5cdb0, now 82122a0
(ttt.pl:0)      Setting up jumplevel bfc5cdbc, was 82122a0

EXECUTING...

(ttt.pl:0)      Entering new RUNOPS level
    =>
(ttt.pl:0)      enter
(ttt.pl:0)      ENTER scope 2 at pp_hot.c:1712
Entering block 0, type BLOCK
    =>
(ttt.pl:0)      nextstate
    =>
(ttt.pl:1)      pushmark
    =>  *
(ttt.pl:1)      const(IV(70))
    =>  *  IV(70)
(ttt.pl:1)      const(IV(80))
    =>  *  IV(70)  IV(80)
(ttt.pl:1)      const(IV(90))
    =>  *  IV(70)  IV(80)  IV(90)
(ttt.pl:1)      pushmark
    =>  *  IV(70)  IV(80)  IV(90)  *
(ttt.pl:1)      gv(main::b)
    =>  *  IV(70)  IV(80)  IV(90)  *  GV()
(ttt.pl:1)      rv2av
    =>  *  IV(70)  IV(80)  IV(90)  *  AV()
(ttt.pl:1)      aassign
    =>
(ttt.pl:1)      nextstate
    =>
(ttt.pl:2)      pushmark
    =>  *
(ttt.pl:2)      pushmark
    =>  **
(ttt.pl:2)      gv(main::b)
    =>  **  GV()
(ttt.pl:2)      rv2av
    =>  **  IV(70)  IV(80)  IV(90)
(ttt.pl:2)      mapstart
(ttt.pl:2)      ENTER scope 3 at pp_ctl.c:933
(ttt.pl:2)      ENTER scope 4 at pp_ctl.c:940
    =>  **  IV(70)  **  IV(80)  IV(90)  *
(ttt.pl:2)      gvsv(main::_)
    =>  **  IV(70)  **  IV(80)  IV(90)  *  IV(70)
(ttt.pl:2)      const(IV(10))
    =>  **  IV(70)  **  IV(80)  IV(90)  *  IV(70)  IV(10)
(ttt.pl:2)      divide
    =>  **  PVNV(70)  **  IV(80)  IV(90)  *  NV(7)
(ttt.pl:2)      scope
    =>  **  PVNV(70)  **  IV(80)  IV(90)  *  NV(7)
(ttt.pl:2)      mapwhile
(ttt.pl:2)      LEAVE scope 4 at pp_ctl.c:1021
(ttt.pl:2)      ENTER scope 4 at pp_ctl.c:1050
    =>  **  NV(7)  IV(80)  **  IV(90)  *
(ttt.pl:2)      gvsv(main::_)
    =>  **  NV(7)  IV(80)  **  IV(90)  *  IV(80)
(ttt.pl:2)      const(PVNV(10))
    =>  **  NV(7)  IV(80)  **  IV(90)  *  IV(80)  PVNV(10)
(ttt.pl:2)      divide
    =>  **  NV(7)  PVNV(80)  **  IV(90)  *  NV(8)
(ttt.pl:2)      scope
    =>  **  NV(7)  PVNV(80)  **  IV(90)  *  NV(8)
(ttt.pl:2)      mapwhile
(ttt.pl:2)      LEAVE scope 4 at pp_ctl.c:1021
(ttt.pl:2)      ENTER scope 4 at pp_ctl.c:1050
    =>  **  NV(7)  NV(8)  IV(90)  ***
(ttt.pl:2)      gvsv(main::_)
    =>  **  NV(7)  NV(8)  IV(90)  ***  IV(90)
(ttt.pl:2)      const(PVNV(10))
    =>  **  NV(7)  NV(8)  IV(90)  ***  IV(90)  PVNV(10)
(ttt.pl:2)      divide
    =>  **  NV(7)  NV(8)  PVNV(90)  ***  NV(9)
(ttt.pl:2)      scope
    =>  **  NV(7)  NV(8)  PVNV(90)  ***  NV(9)
(ttt.pl:2)      mapwhile
(ttt.pl:2)      LEAVE scope 4 at pp_ctl.c:1021
(ttt.pl:2)      LEAVE scope 3 at pp_ctl.c:1027
    =>  *  NV(7)  NV(8)  NV(9)
(ttt.pl:2)      pushmark
    =>  *  NV(7)  NV(8)  NV(9)  *
(ttt.pl:2)      gv(main::a)
    =>  *  NV(7)  NV(8)  NV(9)  *  GV()
(ttt.pl:2)      rv2av
    =>  *  NV(7)  NV(8)  NV(9)  *  AV()
(ttt.pl:2)      aassign
    =>
(ttt.pl:2)      leave
(ttt.pl:0)      POPBLOCK scope 2 at pp_hot.c:1814
Leaving block 0, type BLOCK
(ttt.pl:0)      LEAVE scope 2 at pp_hot.c:1853
(ttt.pl:0)      leaving RUNOPS level
(ttt.pl:0)      popping jumplevel was bfc5cdbc, now 82122a0
(ttt.pl:0)      Setting up jumplevel bfc5cdb0, was 82122a0
(ttt.pl:0)      popping jumplevel was bfc5cdb0, now 82122a0
(ttt.pl:0)      LEAVE scope 1 at perl.c:567
flw@debian:~/perl-5.9.5$

我们可以清晰地看到,在 mapwhile 指令之前,也就是 callback 执行的时候,只是将结果放在了返回堆栈中。
真正修改目标 list,是在 mapwhile 指令执行的时候。
下面看看 mapwhile 的关键代码:

[Copy to clipboard] [ - ]
CODE:
956 PP(pp_mapwhile)
957 {
958     dVAR; dSP;
959     const I32 gimme = GIMME_V;
960     I32 items = (SP - PL_stack_base) - *PL_markstack_ptr; /* how many new items */
....
969     /* if there are new items, push them into the destination list */ <---- 注意这句注释
970     if (items && gimme != G_VOID) {
....
1020     }
1021     LEAVE;                                      /* exit inner scope */
1022
1023     /* All done yet? */
1024     if (PL_markstack_ptr[-1] > *PL_markstack_ptr) {
....
1046     }
1047     else {
1048         SV *src;
1049
1050         ENTER;                                  /* enter inner scope */
....
1062     }

也就是说,callback 将本次调用的结果压入堆栈,mapwhile 指令负责将 callback 的结果从堆栈中弹出来,放入destination list,而 mapwhile 是 Perl 空间 map 运算的实现的一部分。

很典型的 callback 应用,
map 的 callback 除了将本次调用的结果压入堆栈之外,没有做任何其它的修改操作,
更不会受上次调用的影响,因此不能算 closure。