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
| [reply] [d/l] [select] |
|
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.
| [reply] |
|
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. ;) | [reply] [d/l] |
|
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.
| [reply] |
|
| [reply] |
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 | [reply] [d/l] |
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
| [reply] [d/l] |
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....
| [reply] [d/l] [select] |
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);
| [reply] [d/l] |
|
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?
| [reply] [d/l] |
|
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 | [reply] [d/l] [select] |
|
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.
| [reply] [d/l] [select] |
|
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 | [reply] [d/l] [select] |
Re: Forcing array context
by broquaint (Abbot) on Nov 22, 2002 at 14:28 UTC
|
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 | [reply] [d/l] [select] |
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-
| [reply] |
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. | [reply] [d/l] |
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.
| [reply] |
Re: Forcing array context
by John M. Dlugosz (Monsignor) on Nov 22, 2002 at 21:15 UTC
|
| [reply] |
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 | [reply] |