user354134
user354134

Reputation:

Perl: Unwrapping list inside-out doesn't work?

$all="{this, {is, some, {deeply, nested}, text}, for, you}";
while ($all=~s/{([^{}]*?)}/f($1)/seg) {}
sub f {return \{split(",",$_[0])};}
print @{$all};

I expect $all to be a listref whose list contains:

{this, [reference to array], for, you}

Instead, @{$all} is empty.

I'm sure this is something basic, but what am I doing wrong?

Note: I intentionally "golfed" the code to post here (ie, minimal code that shows the problem). A more extensive version is at: https://github.com/barrycarter/bcapps/blob/master/playground.pl

EDIT: Thanks to everyone who answered! Notes:

Upvotes: 2

Views: 204

Answers (3)

jrey
jrey

Reputation: 13

Your text format is easy to transform into Perl, after that you may eval it.

#!/usr/bin/env perl
my $all = "{this, {is, some, {deeply, nested}, text}, for, you}";

$all =~ s/\s*,\s*/','/g;
$all =~ s/'?\{/['/g;
$all =~ s/\}'?/']/g;
my $result = eval $all;

use Data::Dumper;
print Dumper $result;

Upvotes: 0

Greg Bacon
Greg Bacon

Reputation: 139461

Use a parser.

#! /usr/bin/env perl

use warnings;
use strict;

use Data::Dumper;
use Parse::RecDescent;

my $all = "{this, {is, some, {deeply, nested}, text}, for, you}";

my $p = Parse::RecDescent->new(q[
  list: '{' <commit> listitem(s /,/) '}' { $return = $item[3] }
      | <error?>
  listitem: word | list
  word: /\w+/
]);

my $l = $p->list($all);
die "$0: bad list\n" unless defined $l;

$Data::Dumper::Indent = $Data::Dumper::Terse = 1;
print Dumper $l;

Output:

[
  'this',
  [
    'is',
    'some',
    [
      'deeply',
      'nested'
    ],
    'text'
  ],
  'for',
  'you'
]

Upvotes: 2

geekosaur
geekosaur

Reputation: 61369

You can't have $all simultaneously be a string you're iteratively matching on and an arrayref collecting the result of the iteration. $all will end up being something like the string "thisARRAY(0xdeadbeef)foryou", and @$all will use it as a package symbol name, which almost certainly isn't defined, so it will autovivify as an empty list.

Additionally, {} is already a (HASH instead of ARRAY) reference, so you're returning a SCALAR ref to a HASH ref instead of an ARRAY ref as you apparently expect. And {} are special in regexes (foo{1,3} means 1 to 3 repetitions of foo), so you should escape them.

The correct way to do it is to collect into a result list, something like

my @res;
while ($all =~ /\G\{([^{}]*?)\}/sg) {
    push @res, f($1);
}

use warnings and use strict would have told you something was wrong, if not precisely what. Use them. Always.

Upvotes: 4

Related Questions