Re: Create a new operator, get LHS
by Eily (Monsignor) on Aug 25, 2015 at 16:58 UTC
|
Unless you use source filters (which I do not recommend mainly because of the complexity of parsing code, and the fact that the code perl compiles is not the same as the one you wrote) or design your own version of perl, you can't create a new operator.
You can however overload an existing one, and do something like $var ~~ @list where $var would have to be a blessed reference. $var ~~ @list may actually already do what you want if you use the experimental Smartmatch Operator (but that thing is so complex it's probably a bad idea to use it).
Or, if you're OK with the fact that in @list, $var is close enough to $var in @list you could do something like:
use v5.20;
use List::Util qw(any none);
# Allows the first arg to be an array or a ref
# If the second arg is absent, $_ is used instead
sub in:prototype(+_) { any { $_[1] eq $_ } @{ $_[0] } }
sub notin:prototype(+_) { none { $_[1] eq $_ } @{ $_[0] } }
my @list = 'A'..'Z';
my $in = 'C';
say 'IN' if in @list, $in;
$_ = '1';
say 'OUT' if notin \@list;
say 'NOPE' if in @list, '2';
say 'NOPE' if notin @list, 'D';
TIMTOWTDI of course, with or without List::Util, grep or Prototypes, that's just an example to show what can be done (and any and none are more effective than grep because they stop at the first match)
Edit: updated the code because I had written { $_[1] } instead of { $_[1] eq $_ } and added some links
Edit: And of course, unless order is important, the more effective way to check that an element is present in a set is to use a hash instead of an array. | [reply] [d/l] [select] |
Re: Create a new operator, get LHS
by SuicideJunkie (Vicar) on Aug 25, 2015 at 16:51 UTC
|
You mean like:
if (grep {$_ eq $var} @list)
where the var part can be an arbitrarily complex function?
I don't know about the guts, but List::Utils has a bunch of stuff that does similar things you might want to look at.
| [reply] [d/l] |
|
|
perl -E 'say q<Nope> unless grep { $_ eq $c++ } 1..10; say $c'
Nope
10
| [reply] [d/l] |
|
|
Although it is a bit silly with side effects, that code works fine as you demonstrated.
It isn't very complicated, but arbitrarily complex includes 'simple'. You're also free to start an OpenGL window, slam down some matrix transforms based on $_ and use that result as the grep condition.
| [reply] |
Re: Create a new operator, get LHS
by RonW (Parson) on Aug 25, 2015 at 16:59 UTC
|
| [reply] |
Re: Create a new operator, get LHS ( prototype )
by LanX (Saint) on Aug 25, 2015 at 19:13 UTC
|
> so I can use it in whatever implementation I decide to use?
Could you please be more specific and provide some use cases?
Binary operators are difficult even with overloading, but something like
from {List} $scalar
could be easily done with prototypes. ( i.e. sub from (&$) {...} )
Btw: how does Python compare? == or eq ?
| [reply] [d/l] [select] |
Re: Create a new operator, get LHS
by Laurent_R (Canon) on Aug 25, 2015 at 20:50 UTC
|
Hm, not sure of what you are trying to do, but assuming I understood, one possibility might be using the smart match:
DB<38> use v5.10.0;
DB<39> $var = 7;
DB<40> @a = qw/ 4 6 7 9 3/;
DB<41> print "yes" if $var ~~ @a;
yes
(I know it has been deprecated and I would certainly not use that for production code that is supposed to last, but there are cases where you might want to use it nonetheless because it is a short-life project, for example.)
grep is an alternative:
print "yes" if grep { $var == $_} @a;
Or you could use the any function of List::Util module (if your version of that module is recent enough, version 1.33 or above, I think), with just about the same syntax as the grep line above. This should often be faster than grep because any will short-circuit as soon as it has found a matching element, and grep won't.
If your version of List::Util is too old, then try any in List::MoreUtils, any has been around with it for longer. Or you could implement it yourself with List::Util's reduce function (the reduce documentation shows basically how to do it).
With reduce, you could even implement an iterator-based lazy version of grep that will short-circuit for you (but will usually not be faster, except when the list is long and the item found early in the list).
Or you might turn to Perl 6. ;-)
But all the above might just be off-topic if I misunderstood your requirement. Please explain further.
| [reply] [d/l] [select] |
Re: Create a new operator, get LHS
by Jenda (Abbot) on Aug 26, 2015 at 09:14 UTC
|
If you wish for a if ($var in @list) then the @list ought most probably be a hash and the statement should be if (exists $list{$var}).
Jenda
Enoch was right!
Enjoy the last years of Rome.
| [reply] [d/l] [select] |
Re: Create a new operator, get LHS
by shmem (Chancellor) on Aug 26, 2015 at 10:51 UTC
|
How would one get the LHS (in the above case var) so I can use it in whatever implementation I decide to use?
If you want a real infix in operator, you will have to hack the perl parser, namely perly.y, toke.c and related files.
You could overload an existing infix operator with a in subroutine, but in your programs you would still use the overloaded operator (not in), which then just behaves as in, and your $var has to be blessed to that end.
There's a notation which at least looks similar to infix - method call:
$var->in(@list)
- but $var has to be blessed here, too, into the package into which the in method has been compiled:
package in {
sub new { bless \$_[1],$_[0] }
sub in { my $t = shift; scalar grep { $$t eq $_ } @_ }
};
my $var = in->new(7);
print "yup\n" if $var->in( 0 .. 8 );
__END__
yup
Not very tingly. Sorry about that.
perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
| [reply] [d/l] [select] |
|
|
use warnings;
use strict;
my $in = sub {
my $x=shift;
return scalar grep {$x eq $_} @_
};
if ( 5 ->$in (0..9) ) {
print "bin ich drin oder was?";
}
if ( 'x' ->$in (0..9) ) {
print "mennoooooo... :(";
}
/usr/bin/perl -w /tmp/in.pl
bin ich drin oder was?
Compilation finished at Wed Aug 26 15:42:03
(diabolic laughter)
| [reply] [d/l] [select] |
|
|
| [reply] [d/l] |
|
|
Re: Create a new operator, get LHS (Sub::Infix)
by LanX (Saint) on Aug 26, 2015 at 14:09 UTC
|
| [reply] |
Re: Create a new operator, get LHS (indirect method + autoload)
by LanX (Saint) on Aug 26, 2015 at 11:45 UTC
|
just for fun, only for strings and not really meant for serious use =)
use warnings;
use strict;
# true
x in 'v'..'z';
# false
xx in 'v'..'z';
# but variables aren't barewords
my $x='x';
in->$x('v'..'z');
package in;
use Data::Dump qw/pp/;
our $AUTOLOAD;
sub AUTOLOAD {
(my $LHS= $AUTOLOAD) =~ s/in:://;
#print pp [$LHS, @_];
my $res = grep {$LHS eq $_ } @_;
my $not ="";
$not =' not' unless $res;
warn "$LHS$not @_";
return $res;
}
out
/usr/bin/perl -w /tmp/in.pl
x in v w x y z at /tmp/in.pl line 35.
xx not in v w x y z at /tmp/in.pl line 35.
x in v w x y z at /tmp/in.pl line 35.
Compilation finished at Wed Aug 26 13:51:16
| [reply] [d/l] [select] |
|
|
use v5.20;
use Operator qw{in};
use Variables qw{X Y Z};
my @list = 1..10;
(X, Y) = (1, 11);
say X." in @list" if X in @list;
say Y." not in @list" unless Y in \@list;
Z = 'A';
say Z." in list" if Z in ['A'..'C'];
package Variables;
use v5.20;
our %V;
sub import
{
shift;
my $package = caller;
no strict "refs";
for my $var (@_)
{
*{"$package::$var"} = sub:lvalue { @_ ? $_[0]->($V{$var}) : $V{$va
+r} };
}
}
1;
package Operator;
use v5.20;
use List::Util qw{any none};
use Exporter 'import';
our @EXPORT_OK = 'in';
sub in(+)
{
my $list = shift;
return sub { any { $_ eq $_[0] } @$list };
}
1;
1 in 1 2 3 4 5 6 7 8 9 10
12 not in 1 2 3 4 5 6 7 8 9 10
A in list
Edit: fixed the broken link for lvalue subroutines thanks to shmem.
Edit: $V{$var} is what I meant, not $V::{$var} | [reply] [d/l] [select] |
|
|
I like the idea of realizing variables as lvalue functions which evaluate passed code refs!
Thumbs up. :)
Not for normal Perl code, but maybe within internal DSLs which are limited to certain code areas like blocks.
Not sure why you use a hash where a closure var could do...
| [reply] |
|
|
Re: Create a new operator, get LHS
by lonewolf28 (Beadle) on Aug 26, 2015 at 23:44 UTC
|
if ( $var ~~ @whatever ) {
#do something
}
Update:
However, when you do a string check comparison then I don't think smart matching would work.
For example:
#!/usr/bin/python
some_var = 'sa'
words = 'same'
if some_var in words:
print "yes"
In python you can even step through a string like a list and index it. That would not be possible in Perl.
| [reply] [d/l] [select] |
|
|
| [reply] |
Re: Create a new operator, get LHS
by raiph (Deacon) on Sep 11, 2015 at 15:50 UTC
|
In Perl 6, operators are defined by subs. Operator subs have to have an appropriate name and signature. They get automatically added to the Perl 6 grammar in the current lexical scope as soon as their name and signature have been successfully parsed.
For example, infix operators have a name of the form 'infix:<op>' and two parameters. So the code:
sub infix:<foo> ($lhs, $rhs) { dd $lhs };
sub infix:<in> ($lhs, @rhs) { dd @rhs };
1 foo 2;
3 in [3,4];
prints (via `dd`, the built in datadumper):
$lhs = 1
@rhs = [3, 4]
For more details start at the Defining Operators section in the official end user doc.
| [reply] [d/l] [select] |