$escalar = @array? and regexs

W

Winston

Is possible to create an array from an escalar? $var = alfafla;
I want to know if any $var is palindrome. And seems really difficult
using regex, but is very easy if it would be an array.

And what about if I want to check if $var2 has all $var's letters? ($var
=~ /[$var2]+/ ?

And if $var has a letter many times in any position? $var =~
/($letter)*\1/ ?

What is the best book for regular expressions?
 
J

John Bokma

Palindrome:
for (0 .. @Var-1) { ++$bad if lc $Var[$_] ne lc $Var[$#Var-$_] }


for ( 0 .. int( @var / 2 ) ) { .. }

You're counting too many bads IMHO.
 
J

Jürgen Exner

Winston said:
Is possible to create an array from an escalar? $var = alfafla;

Well, yeah, sure. There are many ways depending upon what you want the
content of that array to be.
I want to know if any $var is palindrome. And seems really difficult
using regex, but is very easy if it would be an array.

The easiest way to test that is probably to use reverse().
if ($s eq reverse $s) {....}

jue
 
D

Dr.Ruud

John Bokma schreef:
Mirco Wahab:
[palindrome]
for (0 .. @Var-1) { ++$bad if lc $Var[$_] ne lc $Var[$#Var-$_] }

for ( 0 .. int( @var / 2 ) ) { .. }

for my $i ( 0 .. int( ($#var - 1) / 2 ) )
{
++$bad if lc $Var[$i] ne lc $Var[-1 - $i]
}

If there are 8 elements, then $# is 7, so elements 0..3 need to be
compared with 7..4.
If there are 9 elements, then $# is 8, so elements 0..3 need to be
compared with 8..5.
(I think the int() is implicit, but it doesn't harm much.)

For small arrays (like less than 100 elements or so, or more if the
programmers has counting problems), the "reverse" approach is more
attractive.

To show that a copy is made:

$ perl -wle '@a=(1,2,3); ${@a}[2]++; print @a'
124

$ perl -wle '@a=(1,2,3); ${reverse @a}[2]++; print @a'
123
 
J

John Bokma

Mirco Wahab said:
Thus spoke Dr.Ruud (on 2006-12-07 13:19):
for my $i ( 0 .. int( ($#var - 1) / 2 ) )
{
++$bad if lc $Var[$i] ne lc $Var[-1 - $i]
}

If there are 8 elements, then $# is 7, so elements 0..3 need to be
compared with 7..4.
If there are 9 elements, then $# is 8, so elements 0..3 need to be
compared with 8..5.
(I think the int() is implicit, but it doesn't harm much.)

OK, I checked the code, modified it slightly
and put it into my snippets dir:

my $var = 'Reliefpfeiler'; # something german
my @Var = split '', $var;
print "p'drome"
if not grep lc $Var[$_] ne lc $Var[@Var-1-$_], 0..@Var/2;


As Ruud noted, you need only to check 0 .. ( @Var/2 ) - 1, or 0 .. $#Var -
1 (assuming your array is zero based indexed).

BTW using $var and $Var would confuse the bleep out of me :-D.
 
D

Dr.Ruud

Mirco Wahab schreef:
Dr.Ruud:
for my $i ( 0 .. int( ($#var - 1) / 2 ) )
{
++$bad if lc $Var[$i] ne lc $Var[-1 - $i]
}

If there are 8 elements, then $# is 7, so elements 0..3 need to be
compared with 7..4.
If there are 9 elements, then $# is 8, so elements 0..3 need to be
compared with 8..5.
(I think the int() is implicit, but it doesn't harm much.)

OK, I checked the code, modified it slightly
and put it into my snippets dir:

my $var = 'Reliefpfeiler'; # something german
my @Var = split '', $var;
print "p'drome"
if not grep lc $Var[$_] ne lc $Var[@Var-1-$_], 0..@Var/2;

With 9 elements, "0..@Var/2" is "0..4", which is OK. But with 8 elements
it is also "0..4" where it only needs to be "0..3". The 0 in your
"0..@Var/2" is an index (the minimal one), but the @Var gets you a
count, so use $#Var (the maximal index).

$ perl -wle '
$_ = q/abcba/; print;
@chars = split q//;
print q/palindrome/
unless grep lc $chars[$_]
ne lc $chars[-1 - $_], 0 .. ($#chars - 1) / 2 ;
'

grep returns a list, and that list can be huge.


$ perl -wle '$,=q/,/;
$_ = q/0123-45678901234567890123456789-3210/; print;
@chars = split q//;
for (0 .. ($#chars - 1) / 2 ) {
(++$bad, last) if lc $chars[$_] ne lc $chars[-1 - $_]
}
$bad or print q/palindrome/;
'
 
M

Mirco Wahab

Dr.Ruud said:
Dr.Ruud schreef:


Not really: it still compares the central element to itself.

Right, now we mastered the "big problem" ;-)

Another version without the potentially huge list through grep:

my $piv = int(@Arr/2); # range shifted by +1, 1..N/2, see below

shift(@Arr) eq pop(@Arr) or $piv=0 for 1..$piv;

print "p'drome" if $piv;

(if we can drop the array that we created temporarily)

Regards

Mirco
 
A

anno4000

Mirco Wahab said:
Right, now we mastered the "big problem" ;-)

Another version without the potentially huge list through grep:

my $piv = int(@Arr/2); # range shifted by +1, 1..N/2, see below

shift(@Arr) eq pop(@Arr) or $piv=0 for 1..$piv;

print "p'drome" if $piv;

(if we can drop the array that we created temporarily)

That isn't quite correct, it returns 0 for a single-element array
(which is a palindrome). You're letting the variable $piv do double
duty as an index delimiter and the result indicator. It is quite
typical that this leads to problems in marginal cases. Variant
(written as a sub):

sub is_palindrome {
shift eq pop or return 0 while @_ > 1;
return 1;
}

Anno
 
D

Dr.Ruud

Mirco Wahab schreef:
Dr.Ruud:

Right, now we mastered the "big problem" ;-)

Another version without the potentially huge list through grep:

my $piv = int(@Arr/2); # range shifted by +1, 1..N/2, see
below

shift(@Arr) eq pop(@Arr) or $piv=0 for 1..$piv;

print "p'drome" if $piv;

(if we can drop the array that we created temporarily)

$ perl -wle '
$_ = q(abcdcba) ;
$p = int length() / 2 ; # $p can be 0
print q/palindrome/
if substr($_, 0, $p) eq reverse substr($_, -$p, $p)
'

But how palindromic is an empty string?
 
M

Mirco Wahab

That isn't quite correct, it returns 0 for a single-element array
(which is a palindrome).

A single letter is *always* a palindrome, so
it is somehow a 'degenerated one', which is
not very interesting ;-)
Variant (written as a sub):

sub is_palindrome {
shift eq pop or return 0 while @_ > 1;
return 1;
}


Nice, this can be extended to anonymous sub, like:

my $palindrome = sub{ shift eq pop or return 0 while @_ > 1; 1};

my $var = 'ahcha';

print $var if $palindrome->(split '', $var);


Regards

Mirco
 
M

Mirco Wahab

Dr.Ruud said:
$ perl -wle '
$_ = q(abcdcba) ;
$p = int length() / 2 ; # $p can be 0
print q/palindrome/
if substr($_, 0, $p) eq reverse substr($_, -$p, $p)
'

But how palindromic is an empty string?

As 'palindromic' as a single character ;-)

Now we have a lot solutions on 'how to reverse a string',
this would be a nice worked out chapter in one of the
introductory Perl books ...

Regards

Mirco
 
D

Dr.Ruud

Mirco Wahab schreef:
Dr.Ruud:

As 'palindromic' as a single character ;-)

Now we have a lot solutions on 'how to reverse a string',
this would be a nice worked out chapter in one of the
introductory Perl books ...

Well, I am still waiting for the winner: a for loop comparing single
character substr-s. Without both split and reverse, it could be the
fastest. Unless maybe when the string contains multibyte characters.
 
A

anno4000

Dr.Ruud said:
Mirco Wahab schreef:

Well, I am still waiting for the winner: a for loop comparing single
character substr-s. Without both split and reverse, it could be the
fastest. Unless maybe when the string contains multibyte characters.

Something like this?

sub loop {
my $str = shift;
my $p = int length() / 2;
substr( $str, $_, 1) eq substr( $str, -$_ - 1, 1) or
return 0 for 0 .. $p;
return 1;
}

What makes you think a Perl loop would beat any of the solutions that
do their looping in C? It performs rather poorly (cf. appended code):

Rate loop ruud anno
loop 48651/s -- -55% -69%
ruud 107184/s 120% -- -31%
anno 156392/s 221% 46% --

worse for longer strings. Your optimization (comparing only half-strings)
begins to pay with lengths > 1000 on my machine.

Anno


#!/usr/local/bin/perl
use strict; use warnings; $| = 1;

use Benchmark qw( cmpthese);

goto bench;

check: {
for ( qw( gag gaga reliefpfeiler not_a_palindrome) ) {
my $ans = loop( $_) ? "is" : "isn't";
print "$_ $ans a palindrome\n";
}
}
exit;

bench: {

my @str = q(abcdcba);
print length $str[ 0], "\n";

cmpthese -1, {
ruud => sub { ruud( $_) for @str },
anno => sub { anno( $_) for @str },
loop => sub { loop( $_) for @str },
};
}
exit;

#######################################################################

sub ruud {
$_ = shift;
my $p = int length() / 2 ; # $p can be 0
substr($_, 0, $p) eq reverse substr($_, -$p, $p);
}

sub anno {
$_ = shift;
$_ eq reverse $_;
}

sub loop {
my $str = shift;
my $p = int length() / 2;
substr( $str, $_, 1) eq substr( $str, -$_ - 1, 1) or return 0 for 0 .. $p;
return 1;
}
 
D

Dr.Ruud

(e-mail address removed)-berlin.de schreef:
Dr.Ruud:

What makes you think a Perl loop would beat any of the solutions that
do their looping in C?

You are right of course. I guess I was longing for the C way, working
with the data at hand, without making any copy first.
sub loop {
my $str = shift;
my $p = int length() / 2;

my $p = int length($str) / 2;
substr( $str, $_, 1) eq substr( $str, -$_ - 1, 1) or return 0 for 0 ... $p;
return 1;
}



Another one:

sub regex {
local $_ = $_[0] ;
s/^(.)(.*)\1$/$2/g ;
length() < 2 ;
}

With data like this:

my $str = (join(':', 'a'..'z') . reverse(join(':', 'a'..'z'))) x 100;

it even performs quite well.


Maybe I should create a clever utf8-compliant sublanguage for strings
now, see also String::Strip. Just for fun, and of course incredible
speed.
 
A

anno4000

Dr.Ruud said:
(e-mail address removed)-berlin.de schreef:
[...]
sub loop {
my $str = shift;
my $p = int length() / 2;

my $p = int length($str) / 2;
substr( $str, $_, 1) eq substr( $str, -$_ - 1, 1) or return 0 for 0 .. $p;
return 1;
}

Oh... a bug. Thanks for catching it. Since $_ is aliased to $str in
the benchmarks the result happens to be the same.

Anno
 
D

Dr.Ruud

(e-mail address removed)-berlin.de schreef:
Dr.Ruud:

Oh... a bug. Thanks for catching it. Since $_ is aliased to $str in
the benchmarks the result happens to be the same.

Yes, I had to do some rather selective quoting to make it one.
:)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top