Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

Forcing array context

by jens (Pilgrim)
on Nov 22, 2002 at 05:20 UTC ( [id://215007]=perlquestion: print w/replies, xml ) Need Help??

jens has asked for the wisdom of the Perl Monks concerning the following question:

'perldoc -f scalar' unhelpfully tells me:
There is no equivalent operator to force an expression to be interpolated in list context because in practice, this is never needed. If you really wanted to do so, however, you could use the construction "@{[ (some expression) ]}", but usually a simple "(some expression)" suffices.
I want to use a scalar $string in a very C-like way,
that is I want to run through the @string character
by character, fiddling with chars along the way.
I've tried the above suggestion, as well as:
foo( () = bar() );
but I can't see how either syntax can be used. Any suggestions?

Before you ask why I don't just do this in C, it's because I want
to see if it can be done in Perl.
So there!
--
Microsoft delendum est.

Replies are listed 'Best First'.
Re: Forcing array context
by jarich (Curate) on Nov 22, 2002 at 05:32 UTC
    I want to use a scalar $string in a very C-like way, that is I want to run through the @string character by character, fiddling with chars along the way.
    Try something like
    my @chars = split //, $string;
    Then @chars has each character in it. So:
    foreach my $char (split //, $some_string) { if($char eq " ") { print "it was a space!"; # change all spaces to |s $char = "|"; } }
    or something like that. Then of course you join them together when you're done...
    $string = join("", @chars);

    Hope it helps.

    jarich

      A reasonable solution, but I want to do this without
      invoking the regex engine, so split is strictly taboo...Any other ideas?
      --
      Microsoft delendum est.
        If substr's okay, how about this kind of thing:
        my $string = "This is a very long string"; foreach (0..length($string)) { if(substr($string, $_, 1) eq " ") { # Replace this position (space) with a "|" substr($string, $_, 1, "|"); } } print $string;
        It kinda depends on what you're trying to do, but I think this might help. (Note that you can also replace with the empty string).

        Hope it does.

        jarich

        Update: forgot the length. ;)

        A reasonable solution, but I want to do this without invoking the regex engine
        When you say, "I wanna do $X", and the answer is "by doing $Y", and then you say "I wanna do $X but without doing $Y", you are obligated to provide a reason why the best/easiest way ($Y) is so easily dismissed. How close to doing $Y can you get, for example, and why not $Y? {grin}

        You haven't yet. Please do so.

        -- Randal L. Schwartz, Perl hacker
        Be sure to read my standard disclaimer if this is a reply.

        What's the deal with imposing ridiculous, artificial restrictions? Split is the correct way to do it, so there.

        --
        Regards,
        Helgi Briem
        helgi AT decode DOT is

Re: Forcing array context
by Zaxo (Archbishop) on Nov 22, 2002 at 05:58 UTC

    The @{[$string]} construction does not correspond to what you want. It evaluates to an array of one element, a copy of the value of $string. I don't quite see what you're attempting with the second try. A string in list context is a one element list.

    The perlish way to treat the characters of a string as an array is to produce the array with my @str_ary = split '', $string;, but that does not help you modify the characters of $string in place, as the C char * as array construction would.

    You're best bet is with substr. Letting sub fiddle {} take a character and return whatever you wish,

    my $index; while ( $index < length( $string) ) { substr( $string, $index, 1) = fiddle(substr( $string, $index, 1)); $index++; }
    That isn't nearly as tidy as pointer indexing, but perl is just not as close to the metal as C.

    Note that &fiddle can return empty or strings longer than one character, and the changed length will be handled. That is probably an invitation to code with mysterious behaviors, and non-termination is possible.

    After Compline,
    Zaxo

Re: Forcing array context
by dpuu (Chaplain) on Nov 22, 2002 at 07:01 UTC
    You could try using unpack/pack:
    $foo = "hello, world"; @foo = unpack( ("a" x length($foo)), $foo); @foo = map { bar $_ } @foo; $foo = pack( ("a"x@foo), @foo);
    This isn't an inplace edit, though. --Dave
Re: Forcing array context
by demerphq (Chancellor) on Nov 22, 2002 at 14:12 UTC
    Actually I would say that your restriction on using regexes is a mistake. The reason I say this is that regexes are essentially the perl route to manipulating strings in the way that you want to. Using substr() is a possibility but it is not anywhere near as powerful or efficient as a regex.

    For instance if you want to iterate over the characters of the string, performing some kind of transformation upon them you probably want to do something like

    s/(.)/ord($1)>127 or ord($1)<=32 ? sprintf("%%%02x",ord($1)) : $1/se +;
    The s/// operator is about the best way in perl to iterate over all of the chars in a string (and be able to modify them in place, other means are available if you dont want in place editing) with something close to the efficiency of C. This is primarily because s/(.)/(whatever)/se is roughly equivelent to (using the above example instead of the ...)
    for my $ofs (0..length($str)-1) { my $c=substr($str,$ofs,1); substr($str,$ofs,1)=sprintf("%%%02x",ord($c)) if ord($c)>127 or ord( +$c)<=32; }
    But all of the iteration code, the repeated substr()s etc are all done in the C layer and not in the interpreted opcode layer which means it is much more efficient.

    Before you ask why I don't just do this in C, it's because I want to see if it can be done in Perl.

    Perl is a GPPL/Turing Complete. Thus it can do _anything_ that any other turing complete/GPPL language can do. And of course C is turing complete....

    The real question is how fast was it to write, how maintainable is it, how fast is it to run, and how much did it cost to write. Normally Perl outperforms C when all criteria are taken together. Of course there some things that are much easier in C. But iterating over a character array isnt really one of them.

    --- demerphq
    my friends call me, usually because I'm late....

Re: Forcing array context
by jryan (Vicar) on Nov 22, 2002 at 09:23 UTC

    The easiest way to force something into list context is to use the second example that you posted. Here's an example that I used on this week's Perl Quiz of the Week, to force a globally matching regular expression to match in list context:

    () = ($_[0] =~ /no spoilers :P/g);
      for the benefit of those (or I) who are reading with interest. jryan can you untangle this a bit?.
      ()= I understand as filling an anonymous list with whatever is to the right of =. Forcing list context.
      But what is being evaluated by the regex? just the first element of the @_ array ?
      Deductivly the enclosing ( .... ) parens do magic on @_ but WTF? has anyone ever written a 'golf disassembler' ?

      submersible_toaster
      -they all say 'my computer is frozen!'. just once couldn't one catch fire?
        But what is being evaluated by the regex? just the first element of the @_ array ?
        The regex match is doing just what is in the code - matching the first element of @_ with the regex. The effect of the list context is that whatever is matched is returned e.g
        print map("[$_]", "foo bar baz quux" =~ /b\w+ /g), $/; __output__ [bar ][baz ]
        This is the result of the g modifier in list context without capturing parens. With capturing parens however it returns a list of what was captured e.g
        print map("[$_]", "foo bar baz quux" =~ /(b\w+) /g), $/; __output__ [bar][baz]
        For more info regarding regex matching and context see the Regexp Quote-Like Operators section in perlop.
        HTH

        _________
        broquaint

        I think that the confusion is my fault. You are correct that () = is forcing the matches of the regular expression to fill an anyonomous list (which should get optimized out), and are also correct that the match is being evaluated against the first element of @_ (in this case, the first argument to a subroutine). However, there is no magic being done on @_. The enclosing parens could have been completely left out. Here is my code fleshed out a little more:

        sub expand_number_list { my @matches; () = ($_[0] =~ / (match something) (??{munge the match and push it to @matches}) /xg); return @matches; }

        Generally, the point is that just adding () = will force your expression into list context. Please see Forcing list context for more details.

Re: Forcing array context
by Abigail-II (Bishop) on Nov 22, 2002 at 10:00 UTC
    There is hardly any relationship between $string and @string. It isn't that if you use $string in list context that is somehow becomes @string. Instead, $string in list context just gives you a list of one element, $string being the element.

    If you want to process the characters one by one, the standard idiom is to use:

    foreach my $ch (split // => $string) { .. }

    However, you put yourself under the restriction of not wanting to use regexes. Without specifying why. That's fine by me, but if you like to solve artificial puzzles, have fun on your own!

    Abigail

Re: Forcing array context
by broquaint (Abbot) on Nov 22, 2002 at 14:28 UTC
    I want to use a scalar $string in a very C-like way
    <plug>

    You could always use Data::Pointer e.g

    use Data::Pointer qw( char_ptr ); for(my $p = char_ptr("a string of characters"); $p->deref; $p->incr) { print '[', $p->deref, ']'; } __output__ [a][ ][s][t][r][i][n][g][ ][o][f][ ][c][h][a][r][a][c][t][e][r][s]
    Ok, so it's not terribly C like, but the concept is there. Hopefully I'll be able to grok overload and XS one day and get a real pointer implementation, until then this shoddy implementation will have to do ;)

    </plug>


    HTH

    _________
    broquaint

Re: Forcing array context
by gjb (Vicar) on Nov 22, 2002 at 14:37 UTC

    Have a look at Tie::CharArray, this ties a string to an array of characters.

    Hope this helps, -gjb-

Re: Forcing array context
by BrowserUk (Patriarch) on Nov 22, 2002 at 13:16 UTC

    If I interpret your intent correctly, you want to convert a scalar to an anonymous array of its chars in a manner that allows it to be used as part of another expression without using the regex engine, which I assume to be because this could be part of an expression embedded in a regex itself and you need to avoid recursive calls that cause the regex engine to blow up?

    You could try one of these:

    do{{push@_,chop;redo if length};@_} do{while($£=chop){push@_,$£};@_}

    Both should be usable anywhere an anonymous array can be used. The caveat is that the order of the characters is reversed, but that is easily dealt with.


    Okay you lot, get your wings on the left, halos on the right. It's one size fits all, and "No!", you can't have a different color.
    Pick up your cloud down the end and "Yes" if you get allocated a grey one they are a bit damp under foot, but someone has to get them.
    Get used to the wings fast cos its an 8 hour day...unless the Govenor calls for a cyclone or hurricane, in which case 16 hour shifts are mandatory.
    Just be grateful that you arrived just as the tornado season finished. Them buggers are real work.

Re: Forcing array context
by Thelonius (Priest) on Nov 22, 2002 at 12:17 UTC
    You can do it with substr() as jarich indicates (although the last character is length($string) - 1). If you want to use list syntax with it, you can use Tie. See 208875.

    None of this has anything to do with "list context", though.

Re: Forcing array context
by John M. Dlugosz (Monsignor) on Nov 22, 2002 at 21:15 UTC
Re: Forcing array context
by UnderMine (Friar) on Nov 22, 2002 at 11:39 UTC
    Please can you give more information on fiddling? Most fiddling can be efficiently done with regex so why are you saying you don't want to use them?

    Just a query
    UnderMine

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others meditating upon the Monastery: (9)
As of 2024-04-18 10:11 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found