标量上下文中的glob是一个懒惰的迭代器吗?

时间:2015-05-18 09:05:06

标签: perl

我非常喜欢glob经常被忽视的迭代器功能。

我不清楚的是,即使在标量上下文中使用它,它是否计算/将整个列表加载到内存中。在此示例代码中,while循环不会向屏幕打印任何内容(4 ** 24不是一个小数字):

use strict;
use warnings;
use feature 'say';

my $opt = "{A,B,C,D}" x 24;
say while glob $opt;

我尝试/观察过的事情:

  • 我试图通过编写1 while glob $opt;来消除缓冲是一个问题的可能性,但它仍然需要永远,我最终 Ctrl + C ' out out

  • 通过Windows任务管理器检查我的内存使用情况似乎与我运行脚本时没有任何区别。

  • 运行perl -MO=Deparse仅确认标量上下文中正在使用glob而未指明内存使用情况。

  • 运行perl -MO=Concise会产生以下输出,我不知道如何解读:

    m  <@> leave[1 ref] vKP/REFC ->(end)
    1     <0> enter ->2
    2     <;> nextstate(main 49 -:5) v:%,*,&,{,x*,x&,x$,$,469762048 ->3   
    7     <2> sassign vKS/2 ->8
    5        <2> repeat[t2] sK/2 ->6
    3           <$> const[PV "{A,B,C,D}"] s ->4
    4           <$> const[IV 24] s ->5
    6        <0> padsv[$opt:49,74] sRM*/LVINTRO ->7
    8     <;> nextstate(main 74 -:6) v:%,*,&,{,x*,x&,x$,$,469762048 ->9
    l     <@> leave vK* ->m
    9        <0> enter v ->a
    -        <1> null vKP/1 ->l
    g           <|> and(other->h) vK/1 ->l
    f              <1> defined sK/1 ->g
    e                 <2> sassign sK/2 ->f
    c                    <@> glob[t5] sK/1 ->d
    -                       <0> ex-pushmark s ->a
    a                       <0> padsv[$opt:49,74] s ->b
    b                       <#> gv[*_GEN_0] s ->c
    -                    <1> ex-rv2sv sKRM*/3 ->e
    d                       <#> gvsv[*_] s ->e
    -              <@> lineseq vK ->-
    j                 <@> say vK ->k
    h                    <0> pushmark s ->i
    -                    <1> ex-rv2sv sK/3 ->j
    i                       <#> gvsv[*_] s ->j
    k                 <0> unstack v ->a
    - syntax OK
    

我正在运行ActivePerl 5.16.3。

2 个答案:

答案 0 :(得分:2)

当您观察到第一次glob调用和第一次打印结果之间的延迟时,它似乎不是很懒惰。在循环中在标量上下文 not 中执行glob所花费的时间进一步证实了这一点(在我的例子中,我使用较小的指数使得延迟仍然明显但不会太长):

use strict;
use warnings;
use feature 'say';
use Time::HiRes;

$| = 1;

my $opt = "{A,B,C,D}" x 10;
say Time::HiRes::time;
my $x = glob $opt;
say $x;
say Time::HiRes::time;
say while glob $opt;
say Time::HiRes::time;

答案 1 :(得分:1)

当glob模式包含花括号时,会调用递归函数来扩展大括号。请参阅Perl源代码中的ext/File-Glob/bsd_glob.c *:

/*
 * Expand recursively a glob {} pattern. When there is no more expansion
 * invoke the standard globbing routine to glob the rest of the magic
 * characters
 */
static int
globexp1(const Char *pattern, glob_t *pglob)
{
        const Char* ptr = pattern;
        int rv;

        /* Protect a single {}, for find(1), like csh */
        if (pattern[0] == BG_LBRACE && pattern[1] == BG_RBRACE && pattern[2] == BG_EOS)
                return glob0(pattern, pglob);

        while ((ptr = (const Char *) g_strchr((Char *) ptr, BG_LBRACE)) != NULL)
                if (!globexp2(ptr, pattern, pglob, &rv))
                        return rv;

        return glob0(pattern, pglob);
}

我的C生锈了,但是当我在gdb中运行你的脚本并在这个函数上设置一个断点时,它会被击中很多次,表示非常深的递归,并且通过扩展,表示非惰性行为。希望更熟悉Perl内部人员的人可以详细说明。

*以上版本来自版本5.20.2。