[comp.lang.perl] Comparing numbers and regular expressions

gamin@ireq-robot.hydro.qc.ca (Martin Boyer) (02/02/91)

I'm trying to compare a numeric user id against a list of numbers.
Here's my attempt; it doesn't work, possibly because the regular
expression search doesn't treat the numbers as strings:

$uid_list = "13|1";

$this_uid = 101;

if ($this_uid =~ /^$uid_list$/o) {
    print "Matches!\n";
} else {
    print "Doesn't match.\n";
}

This section prints Matches!, but that's not what I want; what I want
is for the test to succeed only for values of 13 or 1.

Can anybody enlighten me?

Thanks,

-- 
Martin Boyer                            mboyer@ireq-robot.hydro.qc.ca
Institut de recherche d'Hydro-Quebec    mboyer@ireq-robot.uucp
Varennes, QC, Canada   J3X 1S1
+1 514 652-8412

lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) (02/02/91)

In article <5152@s3.ireq.hydro.qc.ca> gamin@ireq-robot.hydro.qc.ca (Martin Boyer) writes:
: 
: I'm trying to compare a numeric user id against a list of numbers.
: Here's my attempt; it doesn't work, possibly because the regular
: expression search doesn't treat the numbers as strings:
: 
: $uid_list = "13|1";
: 
: $this_uid = 101;
: 
: if ($this_uid =~ /^$uid_list$/o) {
:     print "Matches!\n";
: } else {
:     print "Doesn't match.\n";
: }
: 
: This section prints Matches!, but that's not what I want; what I want
: is for the test to succeed only for values of 13 or 1.

Well, look at the pattern you're actually searching for: /^13|1$/
Note that ^ and $ don't automatically distribute over alternatives.
/1$/ matches 101.

You want /^(13|1)$/.  So say /^($uid_list)$/ and it should work.

Note that this is essentially a linear search.  If you're doing lots of
comparisons against a longish list, it'll be much faster to load up
an array to check with:

    for (split(/\|/, $uid_list)) {		# or equivalent
	$valid_uid[$_] = 1;
    }
    ...
    if ($valid_uid[$this_uid]) { ...

If your uids get too large to use a normal array, use an associative array
instead.  A vec() would also work, and save a lot of space over a normal array:

    if (vec($valid_uid, $this_uid, 1)) { ...

Larry

merlyn@iwarp.intel.com (Randal L. Schwartz) (02/02/91)

In article <5152@s3.ireq.hydro.qc.ca>, gamin@ireq-robot (Martin Boyer) writes:
| $uid_list = "13|1";
| 
| $this_uid = 101;
| 
| if ($this_uid =~ /^$uid_list$/o) {
|     print "Matches!\n";
| } else {
|     print "Doesn't match.\n";
| }
| 
| This section prints Matches!, but that's not what I want; what I want
| is for the test to succeed only for values of 13 or 1.
| 
| Can anybody enlighten me?

Yeah.  (I hope.)  Look at the regular expression you are building:

	/^13|1$/

This matches anything that starts with a 13, or anything that ends in
a 1, of which 101 is clearly an example.  What you want instead is:

	/^(13|1)$/

And, backtracking that into your code:

##################################################
$uid_list = "13|1";

$this_uid = 101;

if ($this_uid =~ /^($uid_list)$/o) {
    print "Matches!\n";
} else {
    print "Doesn't match.\n";
}
##################################################

On which I get "Doesn't match.".

I wouldn't do it that way, actually.  I'd opt first for an assoc
array, as in:

##################################################
for (13,1) {
	$cool{$_}++;
}

if ($cool{101}) {
	print "Yes!\n";
} else {
	print "No!\n";
}
##################################################

If the values of array are well behaved, I could go for a bitmap,
like:

##################################################
$cool = "";

for (13,1) {
	vec($cool,$_,1) = 1;
}

if (vec($cool,101,1)) {
	print "Yes!\n";
} else {
	print "No!\n";
}
##################################################

There's More Than One Way To Do It, sez Larry.

print pack("h*",unpack("H*","\244W7G\002\026\346\366G\206V'\002\005V'\306\002\206\0266\266V'\302"))
-- 
/=Randal L. Schwartz, Stonehenge Consulting Services (503)777-0095 ==========\
| on contract to Intel's iWarp project, Beaverton, Oregon, USA, Sol III      |
| merlyn@iwarp.intel.com ...!any-MX-mailer-like-uunet!iwarp.intel.com!merlyn |
\=Cute Quote: "Intel: putting the 'backward' in 'backward compatible'..."====/

rbj@uunet.UU.NET (Root Boy Jim) (02/02/91)

In<5152@s3.ireq.hydro.qc.ca>gamin@ireq-robot.hydro.qc.ca (Martin Boyer) writes:
>$uid_list = "13|1";
>$this_uid = 101;
>if ($this_uid =~ /^$uid_list$/o) {
>    print "Matches!\n";
>} else {
>    print "Doesn't match.\n";
>}
>
>This section prints Matches!, but that's not what I want; what I want
>is for the test to succeed only for values of 13 or 1.

You fell into an age old regular expression trap. You just compiled
the pattern "^13|1$", which matches 13 at BOL or 1 at EOL. It has
nothing to do with numbers, as substitution will show you.

You need to compile /^($uid_list)$/o, or put each list member
explicitly between ^ and $.

>Can anybody enlighten me?

No, but at least I can fix your perl problem for you.
-- 

	Root Boy Jim Cottrell <rbj@uunet.uu.net>
	Close the gap of the dark year in between