代码之家  ›  专栏  ›  技术社区  ›  singingfish

构建“自省”嵌套HTML表

  •  0
  • singingfish  · 技术社区  · 14 年前

    这是我使用Perl中的模板工具包编写的,但它更像是一个通用算法问题。我的基本问题是给定这样的数据结构:

    my @array = (
        [qw /00 01/],
        [qw /10/],
        [qw /20 21/],
        [qw /30 31 32 33 /],
    );
    

    我需要这样的输出(简化为示例):

    <00>
      <10>
        <20> <30>(00/10/20/30)</30> <31>(00/10/20/31)</31>
             <32>(00/10/20/32)</32> <33>(00/10/20/30)</33>
        </20>
        <21> <30>(00/10/21/30)</30> <31>(00/10/21/31)</31>
             <32>(00/10/21/31)</32> <33>(00/10/21/31)</33>
        </21>
      </10>
    </00>
    <01>
      <10>
        <20> <30>(01/10/20/30)</30> <31>(01/10/20/31)</31>
             <32>(01/10/20/32)</32> <33>(01/10/20/33)</33>
        </20>
        <21> <30>(01/10/21/30)</30> <31>(01/10/21/31)</31>
             <32>(01/10/21/32)</32> <33>(01/10/21/33)</33>
        </21>
    </10>
    </01>
    

    这是实际输出的嵌套HTML表的简化示例。中心节点处的路径实际上是要调用另一个子例程以用数据填充嵌套表的参数。我相当肯定原始数组结构的转置是有用的,所以我写道 Array::Transpose::Ragged 今天早些时候发布在CPAN上。

    我管理了一个从内部到外部构建嵌套结构的实现(使用Perl Template Toolkit -见下文),但是当我到达结构的外部时,我就没有机会在中心节点上填充所需的数据。以下是实现它的价值:

    [% SET inner = "(path data should go here)" %]
    [% MACRO process_groups(line, inner) BLOCK %]
    [% FOREACH l IN line %]
    <[% l %]>[% inner %]</[% l %]>
    [% END %]
    [% END %]
    [% WHILE (x = records.pop) %]
    [% inner = process_groups(x, inner) %]
    [% END %]
    [% inner %]
    

    对于我应该采取的方法有什么建议吗?

    更新:

    为了兴趣,我想我应该把接受的答案的TT版本放上去。有点棘手,因为TT不像Perl那么灵活,但下面是:

    #!/usr/bin/env perl
    use warnings;
    use strict;
    use Template;
    my $template = Template->new();
    my @array = (
        [ qw/00 01/ ], [ qw/10/ ],[ qw/20 21/ ], [ qw/30 31 32 33/ ]);
    my $stash = { records => \@array, };
    $template->process(\*DATA, $stash) || die $template->error(), "\n";
    
    __END__
    [% MACRO print_output(data, path_elements) BLOCK; %]
    [% current = data.0; remaining = data.slice(1); %]
    [% FOREACH d IN current %]
    <[% d %]>
    [% IF remaining.size > 0  %]
    [% path_elements.push(d); print_output(remaining, path_elements); %]
    [% SET discard = path_elements.pop %]
    [% ELSE %]
    ([% path_elements.join('/')  _ '/' _ d  %])
    [% END %]
    </[% d %]>
    [% END %]
    [% END %]
    [% SET path = []; print_output(records, path) %]
    

    更好的是,这里是TT中的实际嵌套表结构:

    [% MACRO print_output(data, path_elements) BLOCK; %]
    <table> <tr>
    [% current = data.0; remaining = data.slice(1); %]
    [% FOREACH d IN current %]
    <th>[% d %]</th>
    [% END %] </tr>
    <tr>
    [% FOREACH d IN current %]
    [% IF remaining.size > 0  %]
    <td id="[% d %]">[% path_elements.push(d); print_output(remaining, path_elements); %]</td>
    [% SET discard = path_elements.pop %]
    [% ELSE %]
    <td>([% path_elements.join('/')  _ '/' _ d  %])</td>
    [% END %]
    [% END %]
    </tr></table>
    [% END %]
    [% SET path = []; print_output(records, path) %]
    
    2 回复  |  直到 14 年前
        1
  •  1
  •   FMc TLP    14 年前

    我不确定我是否理解您工作的全部背景,但这里有一个切入点:

    use strict;
    use warnings;
    
    my @array = (
        [ qw/00 01/ ],
        [ qw/10/ ],
        [ qw/20 21/ ],
        [ qw/30 31 32 33/ ],
    );
    print_output(\@array);
    
    sub print_output {
        my ($data, @path_elements) = @_;
        my $level = @path_elements;
        my ($current, @remaining) = @$data;
        for my $d (@$current){
            print '  ' x $level, "<$d>\n";    
            if (@remaining){
                print_output(\@remaining, @path_elements, $d);        
            }
            else {
                print '  ' x ($level + 1), "(", join('/', @path_elements, $d), ")\n";
            }
            print '  ' x $level, "</$d>\n";
        }
    }
    
        2
  •  1
  •   bob.faist    14 年前

    如果您需要模板工具包解决方案,请参见下面的。

    Perl代码:

    use strict;
    use Template;
    
    my @array = (
        [qw /00 01/],
        [qw /10/],
        [qw /20 21/],
        [qw /30 31 32 33/],
    );
    
    my $tt = Template->new(POST_CHOMP => 1);
    
    $tt->process('template.tt', { DATA => \@array }) or die "TT Error : " . $tt->error();
    

    TT模板(固定)(template.tt):

    [% BLOCK display -%]
       [% arr = DATA.$i %]
       [% IF i == DATA.max %]
          [% FOREACH t IN arr -%]
             <[% t %]>
                 [% tmp_c = c.substr(1) %]
                 [% "($tmp_c/$t)" %]
             </[% t %]>
          [% END %]
       [% ELSE %]
          [% FOREACH t IN arr -%]
             <[% t %]>
                [% INCLUDE display i = i+1, c = "$c/$t" %]
             </[% t %]>
          [% END -%]
       [% END %]
    [% END -%]
    
    [% INCLUDE display i = 0, c = '' %]
    
    推荐文章