Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses
 
PerlMonks  

chomp any data structure recursively

by Juerd (Abbot)
on Dec 21, 2001 at 00:25 UTC ( [id://133596]=CUFP: print w/replies, xml ) Need Help??

19:50 <@Petruchio> Right now I'm inclined to code up a subroutine to overload chomp, so that it'll chomp every scalar value it finds recursively throughout a nested data structure.

I could not refrain myself from coding it immediately, so here it is :)

Update (200112202209+0100) It _does_ work, and doesn't use the outer $_. The line containing "#### <<< :-)" explains why. (for-modifier). merlyn was right about this not being easy to maintain. Sorry about that, let's just hope it doesn't need maintenance, then.

Update (200112211546+0100) gbarr uses defined() in his not-recursive version. I should have done so too, so I fixed that mistake. Also made it work with references to references.

sub mychomp; # Needed so mychomp can be used without parens # from within mychomp sub mychomp { ref eq 'ARRAY' ? do { mychomp $_ for @$_ } : ref eq 'HASH' ? do { mychomp $_ for values %$_ } : ref eq 'REF' ? do { mychomp $$_ } : ref eq 'SCALAR' ? eval { defined && chomp $$_ } : ref || defined && chomp for @_ #### <<< :-) } # Useful stuff ends here :) # Testing stuff starts here :) $foo = "scalar\n"; @foo = (["first\n", "second\n", "third\n"], [[ "FIRST\n", "SECOND\n", +"THIRD\n"], [{xyzzy => "1st\n"}],{ xyzzy => "1<sup>st</sup>\n"} ], "Just testing\n", \ $foo, \ \ \ $foo, \ [ "Testing :)\n" ], [[[[[[[[[[[[[[[[[ "DEEEEEPER!\n" ]]]]]]]]]]]]]]]]] ); mychomp @foo; use Data::Dumper; print Data::Dumper->Dump(\@foo); # oh, and $foo is chomped too, of course

Replies are listed 'Best First'.
Re: chomp any data structure recursively
by gbarr (Monk) on Dec 21, 2001 at 19:11 UTC
    I thought I would comment because not one of the solutions given handle a reference to a reference. So here is my non-recursive solution.

    sub mychomp { while (@_) { for (shift) { push @_, grep { ref || eval { defined && chomp; 0 }; } ref eq 'ARRAY' ? @$_ : ref eq 'HASH' ? values %$_ : ref eq 'SCALAR' ? $$_ : ref eq 'REF' ? $$$_ : $_ } } }

    Of course even this is not complete as there could be a reference to a GLOB, which could contain a SCALAR, HASH and ARRAY. But who would use those :)

      I thought I would comment because not one of the solutions given handle a reference to a reference. So here is my non-recursive solution.
      I forgot to handle references to references, will be fixed in a few minutes
      I also admit i forgot to test if it's defined, so I'll change that right away.
      Thanks!

      2;0 juerd@ouranos:~$ perl -e'undef christmas' Segmentation fault 2;139 juerd@ouranos:~$

Re: chomp any data structure recursively
by merlyn (Sage) on Dec 21, 2001 at 01:13 UTC
    Yours is bugged. It looks at the outer $_ in all those ref calls. Just happens to work.

    Here's a non-recursive version:

    sub mychomp { while (@_) { eval { chomp $_[0] }, shift, next unless ref; (push @_, @{shift @_}), next if ref $_[0] eq 'ARRAY'; (push @_, values %{shift @_}), next if ref $_[0] eq 'HASH'; shift; } }

    -- Randal L. Schwartz, Perl hacker


    update: I didn't claim it was a working version. Thanks for the updates in the thread. {grin}

    Also, I didn't see the unusual multi-line backwards-for in the original code, so it appears to be less buggy than I thought. {sigh}

      I think your version is butchering array references, and it doesn't handle scalar references. I think this works:
      sub mychomp { while (@_) { for (shift) { ref or (eval { chomp if defined }, last); ref eq 'SCALAR' and (eval { chomp $$_ if defined $$_ }, last); ref eq 'ARRAY' and (do { push @_, map \$_, @$_ }, last); ref eq 'HASH' and (do { push @_, map \$_, values %$_ }, last); } } }
      Question, though, why are we eval'ing the scalar chomps? chomp warns on undef, but does it fail under some condition? (update: I think I answered my own question...its for constant values which can't be chomped).
      Errr, yours just happens not to work.
      use Data::Dumper; sub mychomp { while (@_) { eval {chomp $_[0] }, shift, next unless ref ; (push @_, @{shift @_}), next if ref $_[0] eq 'ARRAY'; (push @_, values %{shift @_}), next if ref $_[0] eq 'HASH'; shift; } } my $foo = "scalar\n"; my @foo = ( [ "bar\n", "foo\n", "xyzzy\n", { blaat => "hee hee\n", hihi => "hoho\n" } ], {qw(a b c d e f)}, "w00t\n", \$foo, \ "read-only\n" ); mychomp(@foo); print Dumper(\@foo); __END__ $VAR1 = [ 'ARRAY(0x80f3764)', 'HASH(0x81495a8)', 'w00t', 'SCALAR(0x8131fe8)', 'SCALAR(0x813e78c)' ];
      Which $_ is the outer one?
      The refs should see the values of @_. I can't think of a reason why they wouldn't (help me out here :)

      In short, this is the pseudocode:
      sub mychomp { for (@_){ $_ is iterable && mychomp it || $_ is a scalar ref && try to chomp its scalar || $_ is not any other reference && chomp it } }
      I'd be quite surprised my code works with many different structures if it's bugged.

      Could you please give an example of what could go wrong? (of course accompanied by an explanation :)

      Anyway, thanks for pointing out that it's bugged.

      2;0 juerd@ouranos:~$ perl -e'undef christmas' Segmentation fault 2;139 juerd@ouranos:~$

        Looking at just this part:
        sub mychomp { ref eq 'ARRAY' ...
        That's ref of $_, which you have not yet set up, so it's the outer $_ from the caller. Luckily, you didn't test your subroutine with something that needed ref-ARRAY treatment as an argument. {grin}.

        -- Randal L. Schwartz, Perl hacker

      I tracked this thread for a while. merlyn incorrectly calls posted code buggy and supplies replacement code that is buggy and his node gets voted to +6. He doesn't update his node to acknowledge either mistake when it is pointed out and his node still climbs 3 more points to +9 after both errors were pointed out in followups. Now that's what I call personality voting!

        Well, then, he doesn't deserve a best node of the day award for this one, but to be fair, he did acknowlege one (update: now both) of his mistakes, and all I did was take his idea and "fix" it (update: and gbarr just blew mine away :), so although it was buggy starting out, it was an interesting idea to begin with, and I don't begrudge him a few XP for that (as if I cared seriously about XP at all)...

        If someone posts a "you blew it" reply, I'd expect a followup "Hey, you're right" and maybe a fix (depending on time available -- many of us are busy, you know), but if someone (e.g., me) posts a fix, then what's the point in replying to that, especially if you're busy?

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: CUFP [id://133596]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others musing on the Monastery: (7)
As of 2024-04-23 19:17 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found