Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
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 wandering the Monastery: (5)
As of 2024-03-19 09:05 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found