writting an 'in' keyword for perl

D

David

I can't count how many times I've wanted to write a statement like
this:

if ($val in @vals){
# do my stuff
}

Does anyone know of a way to cause a sub call to recieve the
scalar preseeding the sub call as well as the array following?
Essetialy I want to create a keyword called in which checks to see if
a list contains a certain value.

Yes I know I can use something like

my $in = $val eq $_ ? 1 : 0 for (@vals);
if ($in){
# do my stuff
}

but I would really like to be able to write it in english form. I'm
searching for a way to do this because both I would like to be able to
use the english form syntax, but also because it would be an excelent
learning experience for me.

Thank,
David
 
D

David K. Wall

David said:
I can't count how many times I've wanted to write a statement like
this:

if ($val in @vals){
# do my stuff
}

Does anyone know of a way to cause a sub call to recieve the
scalar preseeding the sub call as well as the array following?
Essetialy I want to create a keyword called in which checks to see if
a list contains a certain value.

I don't know of a way to do it exactly like that; I'd do it like this:

# untested
my %hash;
@hash{@vals} = ();
if ( exists $hash{$val} ) {
# do stuff...
}
 
L

l v

David said:
I don't know of a way to do it exactly like that; I'd do it like this:

# untested
my %hash;
@hash{@vals} = ();
if ( exists $hash{$val} ) {
# do stuff...
}

This would work:


@vals = (2 .. 10);

# change $val to 1 or 11 or 20 for testing method
$val = '10';
print "@vals\n$val\n\n";

if ("@vals" =~ m/^$val$/) {
print $&;
}
else {
print "$val not in \@vals\n";
}

Len
 
A

Ala Qumsieh

David said:
I can't count how many times I've wanted to write a statement like
this:

if ($val in @vals){
# do my stuff
}

Does anyone know of a way to cause a sub call to recieve the
scalar preseeding the sub call as well as the array following?

I believe someone (Damian Conway?) already wrote a module to do just this.
Hmmm ... a quick search turns up Quantum::Superpositions which adds an any()
function. So you can do this:

if ($val == any(@vals)) { ... }

I've never used it before, and I don't know how efficient it is. But, given
Damian's reputation, I wouldn't worry about that too much :)
my $in = $val eq $_ ? 1 : 0 for (@vals);

I would do this:

if (grep {$_ eq $val} @vals) { ... }

but this iterates through all of @vals even if the first element matches
$val. Depending on the size of @vals, and how many times you're doing this
operation, this might or might not be a problem for you.

--Ala
 
D

David K. Wall

l v said:
This would work:

It didn't when I copied, pasted, and ran it.
@vals = (2 .. 10);

# change $val to 1 or 11 or 20 for testing method
$val = '10';
print "@vals\n$val\n\n";

if ("@vals" =~ m/^$val$/) {

This will never match unless @vals has only one element and that element is
the number we want.

To get it to work properly the regex should be /\b$val\b/.
 
J

Jürgen Exner

David said:
I can't count how many times I've wanted to write a statement like
this:

if ($val in @vals){
# do my stuff
}

Does anyone know of a way to cause a sub call to recieve the
scalar preseeding the sub call as well as the array following?
Essetialy I want to create a keyword called in which checks to see if
a list contains a certain value.

Yes I know I can use something like

my $in = $val eq $_ ? 1 : 0 for (@vals);
if ($in){
# do my stuff
}

That's ugly. Why not simply
for my $in ( grep {$val==$_}, @vals) {
# do my stuff
}

Or if you don't really care about the value and just want to do some action
if a value is present:
if (grep {$val==$_}, @vals) {
# do my stuff
}

jue
 
P

pkent

I can't count how many times I've wanted to write a statement like
this:

if ($val in @vals){
# do my stuff
}

Does anyone know of a way to cause a sub call to recieve the
scalar preseeding the sub call as well as the array following?
Essetialy I want to create a keyword called in which checks to see if
a list contains a certain value.


Personally I'd either do it as a hash lookup:

my %foo = map { $_ => 1 } @vals;
if ($foo{$val}) {
...


or maybe as an explicit loop setting a flag:

my $boolean = 0;
foreach (@vals) {
if ($_ eq $val) {
$boolean = 1;
last;
}
}
if ($boolean) {
...


That's untested code but it looks right to me :) Note that you may
sometimes want to do either string matches or numerical matches,
according to your problem. Which of the above is better for you might
depend on your exact circumstances, too.

P
 
L

l v

David said:
It didn't when I copied, pasted, and ran it.




This will never match unless @vals has only one element and that element is
the number we want.

To get it to work properly the regex should be /\b$val\b/.

Sorry about that -- good catch, David. I was doing some testing and
forgot to take out the anchors in the regexp before posting.

Greediness might muck it up so OP might needs to experiment with white
space as you suggest with /\b$val\b/

As the OP did not post same code with what could be in @vals, so we are
guessing at a solution.

Len
 
A

Anno Siegel

Jürgen Exner said:
That's ugly. Why not simply
for my $in ( grep {$val==$_}, @vals) { ^
# do my stuff
}

Either "grep $val == $_, @vals" or "grep { $val == $_ } @vals". A block
goes into the indirect object slot.

Anno
 
D

David

Thanks all for your responses. While not really a keyword as I was
hoping, you gave me some excellent alternatives.

Thanks again,
David
 
M

Michele Dondi

I can't count how many times I've wanted to write a statement like
this:

if ($val in @vals){
# do my stuff
}

Not exactly the same thing, but a simple minded approach may work too:


#!/usr/bin/perl -l

use strict;
use warnings;

sub is { bless \(shift), 'brb' }

sub brb::in {
my $test=${shift,};
$test eq $_ and return 1 for @_;
undef;
}

for (qw/foo bar/) {
print if is($_)->in(qw/fred gnat foo/);
}

__END__


Then if you really need to, I guess you may use some source filter to
change

<token> in <EXPR>

into

is->(<token>)->in(<EXPR>)

Not that I have tried source filters myself, but

perldoc Filter::Simple

says it is not so difficult after all. Oh, well just let's try it
soon...


package provides_in;

use strict;
use warnings;
use Filter::Simple;

sub main::is { bless \(shift), 'brb' }

sub brb::in {
my $test=${shift,};
$test eq $_ and return 1 for @_;
undef;
}

FILTER_ONLY code => sub {
s/(\$\w+)\s+in\s+(\@\w+)/is($1)->in($2)/g
};

1;
__END__


#!/usr/bin/perl -l

use strict;
use warnings;
use provides_in;

my @vals=qw/fred gnat foo/;

for (qw/foo bar/) {
print if $_ in @vals;
}
__END__


Wow, it works! Bear in mind though that this is only a very rough
approximation, conjured up in a few minutes!


HTH,
Michele
 
M

Michele Dondi

Not that I have tried source filters myself, but

perldoc Filter::Simple

says it is not so difficult after all. Oh, well just let's try it
soon... [snip]

use Filter::Simple; ....
FILTER_ONLY code => sub {
s/(\$\w+)\s+in\s+(\@\w+)/is($1)->in($2)/g

(please read the previous post for more details)


Of course the snippet of code above was intended just as a quick
experiment. Under more realistic circumstances I guess one would
probably use Text::Balanced, but even though it is fully and well
documented I'm having difficulties understanding how use it to
*replace* occurrences of the "in keyword" in the source code.

To be sure I gave a peek into Switch.pm and I could verify that indeed
it uses Text::Balanced, but it is overly complex for me to understand
exactly how the substitutions are made. Which is somehow obvious,
since the issue it addresses is some orders of magnitude more complex
itself...

Now, I do not have any doubt that reading carefully the docs and
experimenting I could eventually manage to do this myself, but if
anyone already knows how to use Text::Balanced (and in particular
extract_variable(), I think!) to perform tasks of this kind and he/she
could provide a helping hand in this sense, it would be a good
intro/tutorial for me on Text::Balanced, and quite an answer for the
OP!

(FWIW) My naive (*not* working!) approach thus far:


package provides_in;
# -*- Perl -*-

use strict;
use warnings;
use Filter::Simple;
use Text::Balanced qw/:ALL/;

sub main::is { bless \(shift), 'brb' }

sub brb::in {
my $test=${shift,};
$test eq $_ and return 1 for @_;
undef;
}

FILTER_ONLY code => sub {
my ($val, $list);
while ( ($val, undef, $list) =
extract_multiple [\&extract_variable,
'in',
\&extract_variable] ) {
s/\G/is($val)->in($list)/;
}
};

1;
__END__


TIA & a Happy New Year to everyone!
Michele
 
W

Web Surfer

[This followup was posted to comp.lang.perl.misc]

I can't count how many times I've wanted to write a statement like
this:

if ($val in @vals){
# do my stuff
}

Does anyone know of a way to cause a sub call to recieve the
scalar preseeding the sub call as well as the array following?
Essetialy I want to create a keyword called in which checks to see if
a list contains a certain value.

Yes I know I can use something like

my $in = $val eq $_ ? 1 : 0 for (@vals);
if ($in){
# do my stuff
}

but I would really like to be able to write it in english form. I'm
searching for a way to do this because both I would like to be able to
use the english form syntax, but also because it would be an excelent
learning experience for me.

Thank,
David

Until the time comes when Perl has such a feature, I would suggest that
you consider using "hashes". It would be more efficient if you do a lot
of searching of the kind you described.


%hash1 = ( "key1" => "value1" , "key2" => "value2" , "key3" =>
"value3" );

if ( exists $hash1{$somekey} ) {
print "Found it...\n";
}
 
M

Malcolm Dew-Jones

Michele Dondi ([email protected]) wrote:
: On Wed, 31 Dec 2003 09:11:48 +0100, Michele Dondi

: >Not that I have tried source filters myself, but
: >
: > perldoc Filter::Simple
: >
: >says it is not so difficult after all. Oh, well just let's try it
: >soon...
: [snip]

: > use Filter::Simple;
: ...
: > FILTER_ONLY code => sub {
: > s/(\$\w+)\s+in\s+(\@\w+)/is($1)->in($2)/g

: (please read the previous post for more details)


: Of course the snippet of code above was intended just as a quick
: experiment. Under more realistic circumstances I guess one would
: probably use Text::Balanced, but even though it is fully and well
: documented I'm having difficulties understanding how use it to
: *replace* occurrences of the "in keyword" in the source code.

You wouldn't need to worry about balancing anything if you ignored the
whole object suggestion, and just predeclare an `in' function, and then
used the filter to swap the value and the `in' keyword.

Make the filter look for ` <token> in ' and convert it into `in <token>, '


if ( $val in qw/what ever/ )

becomes

if ( in $val, qw/what ever/ )


which I think should be valid perl if `in' is a predeclared subroutine and
if the rest of the code is correctly formatted (just like normal when
writing any code).

Personally though, I like the

if ( is($value)->in(args) )

syntax of an earlier sugestion. I think that that reads just as nicely as
the original `if ( value in args )' example, and so would fulfil the
original desire for a nice way to write the code, but without any jiggery
pokery.

It also has obvious extensions to other situations

is($value)->between(x,y)

is($value)->bigger_than_all_of(@whatever)

are(@values)->amongst(@bigger_set)

(In fact you could imagine a general purpose module with the sole
purpose of providing a bunch of such routines.)
 
B

Ben Morrow

Michele Dondi ([email protected]) wrote:
: Of course the snippet of code above was intended just as a quick
: experiment. Under more realistic circumstances I guess one would
: probably use Text::Balanced, but even though it is fully and well
: documented I'm having difficulties understanding how use it to
: *replace* occurrences of the "in keyword" in the source code.

You wouldn't need to worry about balancing anything if you ignored the
whole object suggestion, and just predeclare an `in' function, and then
used the filter to swap the value and the `in' keyword.

Make the filter look for ` <token> in ' and convert it into `in <token>, '


if ( $val in qw/what ever/ )

becomes

if ( in $val, qw/what ever/ )

You still need to find a balanced <token>. What about

if ( ($val + (1+2)) in qw/3 4 5/ )

?
which I think should be valid perl if `in' is a predeclared subroutine and
if the rest of the code is correctly formatted (just like normal when
writing any code).

Personally though, I like the

if ( is($value)->in(args) )

syntax of an earlier sugestion.

I would, were it not that it has *way* too much punctuation. I think
that Quantum::Superpositions::any is probably the way to go:

if ( $value eq any(args) )

, which has the advantage of allowing you to specify the comparison
operator. OTOH, for 'eq' this might work:

{{

package in;

sub AUTOLOAD {
shift;
(my $is = $AUTOLOAD) =~ s/^in:://;
return grep { $_ eq $is } @_;
}

}}

if ( $a in qw/what ever/ ) {
# do stuff
}

Ben
 
M

Michele Dondi

: Of course the snippet of code above was intended just as a quick
: experiment. Under more realistic circumstances I guess one would
: probably use Text::Balanced, but even though it is fully and well
: documented I'm having difficulties understanding how use it to
: *replace* occurrences of the "in keyword" in the source code.

You wouldn't need to worry about balancing anything if you ignored the
whole object suggestion, and just predeclare an `in' function, and then
used the filter to swap the value and the `in' keyword.

Make the filter look for ` <token> in ' and convert it into `in <token>, '

That doesn't make much difference. More precisely, it doesn't make
*any* difference: the whole point is that identifying <token> is not
that easy by means of a simple regex[*], whereas Text::Balanced
provides routines made just for that (e.g. extract_variable()).

Now, if you read my other post on the subject, you'll find that I
tried to use said module, but I made a ridiculous error the reasons
for which I'm not discussing here.

To briefly sum up the story, I did some more experiments with
Text::Balanced, and at least called the appropriate sub on the text it
should operate on! Still had problems with it, though: to be fair I'm
having some doubts I really understood what extract_multiple()
*should* do...

Last, I *had* to dedicate myself to something else and forgot about
Text::Balanced. Also, I have virtually no time to do any more
experiments, so, I know it's not that nice coming here and saying
"hey, just do this for me!", but if anyone is knowledgeable about that
module and knows how to, loosely speaking, "extract <variable>, 'in',
<variable>" *and* substitute in the text (I thought s/\G// would do!)
with a suitable expression, I'd be really grateful.


[*] Until extended regexen will be made available to that effect ;-)


Michele
 
D

Darren Dunham

I believe someone (Damian Conway?) already wrote a module to do just this.
Hmmm ... a quick search turns up Quantum::Superpositions which adds an any()
function. So you can do this:
if ($val == any(@vals)) { ... }
I've never used it before, and I don't know how efficient it is. But, given
Damian's reputation, I wouldn't worry about that too much :)

I think the idea is that you connect it to an actual quantum
superposition computing device as the backend and it runs in O(1) time.

If you don't have one, you have to rely on the code emulation which is
very likely O(n) time.
 

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,773
Messages
2,569,594
Members
45,119
Latest member
IrmaNorcro
Top