Reputation:
$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:
The real f() has side effects, updates dbs, etc, so I really do have to call it. It doesn't just change a list into something else. My bad for not mentioning this.
I was exporting from Mathematica so "{a,b,c}" is a list, not a hash. Again, mea culpa for not mentioning this.
I know the "normal" way to do this is recursive: process each element, and if an element is a list, call f() on the list itself. I was trying to do "unfold" the recursion to avoid splitting nested "{". If you work inside out, you never have to count "{" when parsing.
An interesting other application would be a one-line XML parser (almost).
Giving geekosaur the checkmark for pointing out what was wrong and why my approach is probably wrong.
I think I'll try the Parser approach or even jrey's s/{/[ approach.
Upvotes: 2
Views: 204
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
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
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