For Programmers: Free Programming Magazines  


Home > Archive > PERL Beginners > October 2004 > Recursively counting a matching pattern on a single line.









You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

 

Author Recursively counting a matching pattern on a single line.
S.A. Birl

2004-10-27, 8:55 pm

Given a bookmark:

<DT><A HREF="http://www.perl.com/CPAN-local/doc/FAQs/FAQ/PerlFAQ.html" ADD_DATE="897592292" LAST_VISIT="982769648" LAST_MODIFIED="982769648" ID="rdf:#$rsy5Z">PERL FAQ</A>


Wondering if it's possible to have 2 counters that would keep track of the
number of < and > encountered.

Im looking to see if there's a balance of < and >, and scream as soon as
there isnt a balance.

So given the above bookmark, I know that there's 3 < and 3 >
and the pattern is basically <><><> and not <<>> or >><, etc.

Thanks
Birl
Bob Showalter

2004-10-27, 8:55 pm

S.A. Birl wrote:
> Given a bookmark:
>
> <DT><A
> HREF="http://www.perl.com/CPAN-local/doc/FAQs/FAQ/PerlFAQ.html"
> ADD_DATE="897592292" LAST_VISIT="982769648" LAST_MODIFIED="982769648"
> ID="rdf:#$rsy5Z">PERL FAQ</A>
>
>
> Wondering if it's possible to have 2 counters that would keep track
> of the
> number of < and > encountered.
>
> Im looking to see if there's a balance of < and >, and scream as soon
> as
> there isnt a balance.
>
> So given the above bookmark, I know that there's 3 < and 3 >
> and the pattern is basically <><><> and not <<>> or >><, etc.


If you're specifically looking whether the brackets follow the pattern
<><><>, could you just do something like:

my $brackets = join '', $bookmark =~ /[<>]/g;
die "Bad pattern" unless $brackets eq '<><><>';
Brian Barto

2004-10-27, 8:55 pm

Assign each line to the $_ variable and try this to get the number of
instances of < and >

The match operator works on $_ by default. If you match on < or > globally
and assign it to an array like so:

@rights = m/\</g;
@lefts = m/\>/g;

You are left with arrays that contain all the matches. Now all you need to
do is count the number of elements in each array like so:

$number_of_rights = $#rights;
$number_of_lefts = $#lefts;

Then compare them:

if ($number_of_rights != $number_of_lefts)
{
## scream here!!
}

-----Original Message-----
From: S.A. Birl [mailto:sbirl+PERL@concept.temple.edu]
Sent: Wednesday, October 27, 2004 3:23 PM
To: beginners@perl.org
Subject: Recursively counting a matching pattern on a single line.


Given a bookmark:

<DT><A HREF="http://www.perl.com/CPAN-local/doc/FAQs/FAQ/PerlFAQ.html"
ADD_DATE="897592292" LAST_VISIT="982769648" LAST_MODIFIED="982769648"
ID="rdf:#$rsy5Z">PERL FAQ</A>


Wondering if it's possible to have 2 counters that would keep track of the
number of < and > encountered.

Im looking to see if there's a balance of < and >, and scream as soon as
there isnt a balance.

So given the above bookmark, I know that there's 3 < and 3 >
and the pattern is basically <><><> and not <<>> or >><, etc.

Thanks
Birl

--
To unsubscribe, e-mail: beginners-unsubscribe@perl.org
For additional commands, e-mail: beginners-help@perl.org
<http://learn.perl.org/> <http://learn.perl.org/first-response>

S.A. Birl

2004-10-27, 8:55 pm

On Oct 27, brian.barto@spectrum-health.org (nospam-brian.barto@spectrum-hea...:

Brian: Assign each line to the $_ variable and try this to get the number of
Brian: instances of < and >
Brian:
Brian: The match operator works on $_ by default. If you match on < or > globally
Brian: and assign it to an array like so:
Brian:
Brian: @rights = m/\</g;
Brian: @lefts = m/\>/g;
Brian:
Brian: You are left with arrays that contain all the matches. Now all you need to
Brian: do is count the number of elements in each array like so:
Brian:
Brian: $number_of_rights = $#rights;
Brian: $number_of_lefts = $#lefts;
Brian:
Brian: Then compare them:
Brian:
Brian: if ($number_of_rights != $number_of_lefts)
Brian: {
Brian: ## scream here!!
Brian: }



Great! That works. But I was looking to get a little trickier with it.


If I had:
<DT <A HREF="http://www.perl.com/CPAN-local/doc/FAQs/FAQ/PerlFAQ.html" ADD_DATE="897592292" LAST_VISIT="982769648" LAST_MODIFIED="982769648" ID="rdf:#$rsy5Z">PERL FAQ</A>

I wanted it to scream when it reached the 2nd < ; stopping the pattern
matching from continuing any further. Is that possible, or would
something more complex be needed?

If it's too complex, I'll just with what I have for now.


Thanks
Birl

Please do not CC me responses to my own posts.
I'll read the responses on the list.
Brian Barto

2004-10-27, 8:55 pm

Great! That works. But I was looking to get a little trickier with it.


----------------

If you want to make sure they are alternating like <><><> etc... I would do
this:

$_ = $line;

@syms = m/[<>]/g;
$string = join("", @syms);
if ($strings !~ m/^<(>< )*>$/)
{
## Scream here!
}

The regular expression:

m/^<(>< )*>$/

will ensure that it starts with < and ends with > and anything in between
will be "><" which I think should do the trick. That logic is pretty hairy
though and I could be missing something.
Brian Barto

2004-10-27, 8:55 pm

Correction: "$strings" in the if statement should be "$string"

-----Original Message-----
From: brian.barto@spectrum-health.org
[mailto:brian.barto@spectrum-health.org]
Sent: Wednesday, October 27, 2004 4:04 PM
To: beginners@perl.org
Subject: RE: Recursively counting a matching pattern on a single line.


Great! That works. But I was looking to get a little trickier with it.


----------------

If you want to make sure they are alternating like <><><> etc... I would do
this:

$_ = $line;

@syms = m/[<>]/g;
$string = join("", @syms);
if ($strings !~ m/^<(>< )*>$/)
{
## Scream here!
}

The regular expression:

m/^<(>< )*>$/

will ensure that it starts with < and ends with > and anything in between
will be "><" which I think should do the trick. That logic is pretty hairy
though and I could be missing something.

--
To unsubscribe, e-mail: beginners-unsubscribe@perl.org
For additional commands, e-mail: beginners-help@perl.org
<http://learn.perl.org/> <http://learn.perl.org/first-response>

S.A. Birl

2004-10-27, 8:55 pm

On Oct 27, brian.barto@spectrum-health.org (nospam-brian.barto@spectrum-hea...:

Brian:
Brian: If you want to make sure they are alternating like <><><> etc... I would do
Brian: this:
Brian:
Brian: $_ = $line;
Brian:
Brian: @syms = m/[<>]/g;
Brian: $string = join("", @syms);
Brian: if ($strings !~ m/^<(>< )*>$/)
Brian: {
Brian: ## Scream here!
Brian: }
Brian:
Brian: The regular expression:
Brian:
Brian: m/^<(>< )*>$/
Brian:
Brian: will ensure that it starts with < and ends with > and anything in between
Brian: will be "><" which I think should do the trick. That logic is pretty hairy
Brian: though and I could be missing something.



Wouldnt m/[<>]/g literally match <> and not <characters>?

Why wouldnt it be m/[<.+>]/g ?



Thanks
Birl

Please do not CC me responses to my own posts.
I'll read the responses on the list.
Brian Barto

2004-10-27, 8:55 pm


Wouldnt m/[<>]/g literally match <> and not <characters>?

Why wouldnt it be m/[<.+>]/g ?



Thanks
Birl

-----------

Someone correct me if I'm wrong but putting characters inside brackets []
defines a character class. Or a group of characters you want to match on,
not necessarily in that order.

For instance m/[0-9]/g matches on any single digit number, not "0123456789"
Gunnar Hjalmarsson

2004-10-27, 8:55 pm

S.A. Birl wrote:
> Wondering if it's possible to have 2 counters that would keep track
> of the number of < and > encountered.
>
> Im looking to see if there's a balance of < and >, and scream as soon
> as there isnt a balance.
>
> So given the above bookmark, I know that there's 3 < and 3 > and
> the pattern is basically <><><> and not <<>> or >><, etc.


Assuming that you are actually not interested in *counting* them, this
is one approach:

print "Oops!\n" unless $string =~ /^[^<>]*(?:(?:<[^<>]*> )*[^<>]*)*$/;

--
Gunnar Hjalmarsson
Email: http://www.gunnar.cc/cgi-bin/contact.pl
John W. Krahn

2004-10-27, 8:55 pm

brian.barto@spectrum-health.org wrote:
> Assign each line to the $_ variable and try this to get the number of
> instances of < and >
>
> The match operator works on $_ by default. If you match on < or > globally
> and assign it to an array like so:
>
> @rights = m/\</g;
> @lefts = m/\>/g;


See the FAQ for a more efficient way to do this.

perldoc -q "How can I count the number of occurrences of a substring within a
string"


> You are left with arrays that contain all the matches. Now all you need to
> do is count the number of elements in each array like so:
>
> $number_of_rights = $#rights;
> $number_of_lefts = $#lefts;


An array in scalar context returns the number of elements in that array. You
are using the index of the last element of the array which *may* (or may not!)
be one less than the number of elements.


John
--
use Perl;
program
fulfillment
John W. Krahn

2004-10-27, 8:55 pm

S.A. Birl wrote:
> On Oct 27, brian.barto@spectrum-health.org (nospam-brian.barto@spectrum-hea...:
>
> Brian: The regular expression:
> Brian:
> Brian: m/^<(>< )*>$/
> Brian:
> Brian: will ensure that it starts with < and ends with > and anything in between
> Brian: will be "><" which I think should do the trick. That logic is pretty hairy
> Brian: though and I could be missing something.
>
> Wouldnt m/[<>]/g literally match <> and not <characters>?


Anything inside [ and ] is a character class and will match either '<' or '>'
anywhere in the string.

> Why wouldnt it be m/[<.+>]/g ?


That will match the characters '<' or '.' or '+' or '>'.


John
--
use Perl;
program
fulfillment
Zeus Odin

2004-10-27, 8:55 pm

What I think you are actually saying is that you want to know when you
encounter two <'s or two >'s in a row You then want the program to alert and
exit. The following accomplishes this. Apologies if I misunderstood.
-ZO

-----

#!/usr/bin/perl
use warnings;
use strict;

my $count;
my $text = '<DT <A HREF=' .
'"http://www.perl.com/CPAN-local/doc/FAQs/FAQ/PerlFAQ.html" ' .
'ADD_DATE="897592292" LAST_VISIT="982769648" ' .
'LAST_MODIFIED="982769648" ID="rdf:#$rsy5Z">PERL FAQ</A>';

for my $pos(0 .. length($text)-1) {
$count++ if substr($text, $pos, 1) eq '<';
$count-- if substr($text, $pos, 1) eq '>';
die "Imbalanced tags at position $pos\n" if abs $count > 1;
}
print "Tags are balanced!\n"


"S.A. Birl" <sbirl+PERL@concept.temple.edu> wrote in message ...

> Great! That works. But I was looking to get a little trickier with it.
>
>
> If I had:
> <DT <A HREF="http://www.perl.com/CPAN-local/doc/FAQs/FAQ/PerlFAQ.html"

ADD_DATE="897592292" LAST_VISIT="982769648" LAST_MODIFIED="982769648"
ID="rdf:#$rsy5Z">PERL FAQ</A>
>
> I wanted it to scream when it reached the 2nd < ; stopping the pattern
> matching from continuing any further. Is that possible, or would
> something more complex be needed?



Gunnar Hjalmarsson

2004-10-27, 8:55 pm

Gunnar Hjalmarsson wrote:
>
> print "Oops!\n" unless $string =~ /^[^<>]*(?:(?:<[^<>]*> )*[^<>]*)*$/;


Hmm.. That got unnecessarily complicated. Make it:

print "Oops!\n" unless $string =~ /^[^<>]*(?:<[^<>]*>[^<>]*)*$/;

--
Gunnar Hjalmarsson
Email: http://www.gunnar.cc/cgi-bin/contact.pl
Sponsored Links







Also available: Server administration forum archive | Web Design forum archive | Software forum archive | Hardware reviews archive

Copyright 2008 codecomments.com