Looping through perl constant - perl

I have the following code in perl:
package SignatureScheme;
use strict;
use warnings;
use constant {
SHA256_RSA_V1 => 'SHA256-RSA-V1',
SHA256_HMAC_V1 => 'SHA256-HMAC-V1',
};
How can I loop through the constants listed above and compare them to a string?

For one-time comparison:
my $string = "xyz";
my $found = grep ($_ eq $string) (SHA256_RSA_V1, SHA256_HMAC_V1);
Constants are merely subroutines returning your strings, therefore you can use them alomst anywhere where you would have used the strings themselves.
For repeated comparisons, to improve performance, use hash lookups.
my %lookup_hash = map {($_=>1)} (SHA256_RSA_V1, SHA256_HMAC_V1);
foreach my $lookup_string (#lookup_strings) {
if ($lookup_hash{$lookup_string}) { #do your thing }
}

my #schemes = (SHA256_RSA_V1, SHA256_HMAC_V1);
foreach my $scheme (#schemes) {
if ($scheme eq $string) {
# do something
}
}

Related

Hash lookup with an array of keys?

I'd like to see if a hash contains any of a list of keys. Currently I have:
if (grep {$me_hash{$_}} #me_list){
...
but I figure it's inefficient because it doesn't quit on the first match. Is there a better, more idiomatic way to do it?
kthxbi
You can use List::MoreUtils's any
use List::MoreUtils qw(any);
if (any { $me_hash{$_} } #me_list) {
Which presumably short circuits on the first match. This function is rather simple, looking like this:
sub any (&#) {
my $f = shift;
foreach ( #_ ) {
return YES if $f->();
}
return NO;
}
Where YES and NO are defined as
use constant YES => ! 0;
use constant NO => ! 1;
Meaning you can swing your own version of this with something like
sub is_in {
my ($href, #list) = #_;
for (#list) {
return 1 if $href->{$_};
}
return 0;
}
Note that the statement you are using $me_hash{$_} can return false for values you might not consider false, such as the empty string, or zero 0.
List::Util unlike List::MoreUtils is core module,
use List::Util qw(first);
if (defined first {$me_hash{$_}} #me_list) { .. }
and if you don't want to use any of external modules,
my $any = sub{ $me_hash{$_} and return 1 for #_; 0 }->(#me_list);
if ($any) { .. }

Perl: Syntactical Sugar for Latter Coderef Arguments?

Using sub prototypes, we can define our own subs that look like map or grep. That is, the first coderef argument has shorter syntax than a normal anonymous sub. For example:
sub thunked (&) { $_[0] }
my $val = thunked { 2 * 4 };
Works great here, since the first argument is the coderef. For latter arguments however, it simple won't parse properly.
I made a with sub designed to make writing GTK2 code cleaner. It's meant to look like this (untested since it's hypothetical code):
use 5.012;
use warnings;
use Gtk2 '-init';
sub with ($&) {
local $_ = $_[0];
$_[1]->();
$_;
}
for (Gtk2::Window->new('toplevel')) {
$_->set_title('Test Application');
$_->add(with Gtk2::VBox->new {
my $box = $_;
$box->add(Gtk2::Button->new("Button $_")) for (1..4);
});
$_->show_all;
}
Gtk2->main;
It doesn't work because with needs to take the block as a first argument for the nice syntax to work. Is there any way to pull it off?
The module Devel::Declare contains tools for extending Perl's syntax in a relatively safe way.
Using Devel::Declare you would create a hook on the with token, which will stop the parser when it reaches that word. From there, you have control over the parser and you can read ahead until you reach a { symbol. At that point, you have what you need to work with, so you rewrite it into valid Perl, and pass it back to the parser.
in the file With.pm:
package With;
use warnings;
use strict;
use Devel::Declare;
sub import {
my $caller = caller;
Devel::Declare->setup_for (
$caller => {with => {const => \&parser}}
);
no strict 'refs';
*{$caller.'::with'} = sub ($&) {
$_[1]() for $_[0];
$_[0]
}
}
our $prefix = '';
sub get {substr Devel::Declare::get_linestr, length $prefix}
sub set { Devel::Declare::set_linestr $prefix . $_[0]}
sub parser {
local $prefix = substr get, 0, length($_[0]) + $_[1];
my $with = strip_with();
strip_space();
set "scalar($with), sub " . get;
}
sub strip_space {
my $skip = Devel::Declare::toke_skipspace length $prefix;
set substr get, $skip;
}
sub strip_with {
strip_space;
my $with;
until (get =~ /^\{/) {
(my $line = get) =~ s/^([^{]+)//;
$with .= $1;
set $line;
strip_space;
}
$with =~ s/\s+/ /g;
$with
}
and to use it:
use With;
sub Window::add {say "window add: ", $_[1]->str}
sub Window::new {bless [] => 'Window'}
sub Box::new {bless [] => 'Box'}
sub Box::add {push #{$_[0]}, #_[1..$#_]}
sub Box::str {"Box(#{$_[0]})"}
sub Button::new {"Button($_[1])"}
with Window->new {
$_->add(with Box->new {
for my $num (1 .. 4) {
$_->add(Button->new($num))
}
})
};
Which prints:
window add: Box(Button(1) Button(2) Button(3) Button(4))
A completely different approach would be to skip the with keyword altogether and write a routine to generate constructor subroutines:
BEGIN {
for my $name (qw(VBox)) { # and any others you want
no strict 'refs';
*$name = sub (&#) {
use strict;
my $code = shift;
my $with = "Gtk2::$name"->new(#_);
$code->() for $with;
$with
}
}
}
and then your code could look like
for (Gtk2::Window->new('toplevel')) {
$_->set_title('Test Application');
$_->add(VBox {
my $box = $_;
$box->add(Gtk2::Button->new("Button $_")) for (1..4);
});
$_->show_all;
}
One way that you could deal with it is to add a fairly useless keyword:
sub perform(&) { $_[0] }
with GTK2::VBox->new, perform { ... }
where perform is really just a sugarier alternative to sub.
Another way is to write a Devel::Declare filter or a Syntax::Keyword:: plugin to implement your with, as long as you have some way to tell when you're done parsing the with argument and ready to start parsing the block — balanced parentheses would do (so would an opening curly brace, but then hashes become a problem). Then you could support something like
with (GTK2::VBox->new) { ... }
and let the filter rewrite it to something like
do {
local $_ = GTK2::VBox->new;
do {
...;
};
$_;
}
which, if it works, has the advantage of not actually creating a sub, and thus not interfering with #_, return, and a few other things. The two layers of do-age I think are necessary for being able to install an EndOfScope hook in the proper place.
The obvious disadvantages of this are that it's tricky, it's hairy, and it's a source filter (even if it's a tame one) which means there are problems you have to solve if you want any code using it to be debuggable at all.

Perl Comparison with Bot::BasicBot( testing Equality )

I've figured it out it appears that I was testing each key individually and thats why it kept saying i wasn't in the Operators list.
trick was to move the else statement out of foreach and to change it to a if statement.
then a rather nasty hack inside the test for not equal
original:
if($config->{'OP'}[$key] ne $message->{who})
new:
if($config->{'OP'}[$key-1] ne $message->{who})
final complete code:
#!/usr/bin/perl
use strict;
use warnings;
package kbot;
use base qw(Bot::BasicBot);
use YAML;
use Data::Dumper;
my $bot = kbot->new(
server => 'irc.saurik.com',
channels => ['#spam','#kbot'],
nick => 'kbot',);
sub reload{
system("perl kbot.pl");
}
sub said {
my ($self, $message) = #_;
my $config = YAML::LoadFile('kelbot.yml');
if($message->{body} =~ 'reload'){
reload();
}
if($message->{body} =~ 'opme'){
foreach $::key (keys $config->{OP}){
print $config->{OP}[$::key],"\n";
if($config->{OP}[$::key] eq $message->{who}){
$bot->mode($message->{channel}.' +o '.$message->{who});
} #end of if op
} #end of foreach
if($config->{OP}[$::key-1] ne $message->{who}){
$bot->say( channel => $message->{channel},
body => 'You aren\'t in the Operators list.',
address => $message->{who},
);
} #end of optest
} #end of opme
} #end of said
sub chanjoin {
my ($self, $message) = #_;
return 'kbot now online!';
}
$bot->run();
There are much better ways of doing that in perl than trying to create a c-esque loop. From the code it looks like $config->{'OP'} is an array, but I'm a bit confused by your use of the array subscript ([]) and keys on it at the same time. If it is an array then just use grep
if (grep { $_ eq $message->{who} } #{ $config->{OP} }) {
$bot->mode("$message->{channel} +o $message->{who}");
} else {
$bot->say(channel => $message->{channel},
body => q{You aren't in the Operators list.},
address => $message->{who});
}
This code would completely replace your foreach loop. The grep command is the thing that loops over the entire list and finds whether $message->{who} appears in it. Since that is done so easily in a single statement, the if condition about what to do in the case of it appearing or not is very straightforward.

How do I use `lock_hash_recurse` in Perl?

In continue to the discussion here, I'm havind some trouble with lock_hash_recurse as illustrated below:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
use Hash::Util qw (lock_keys);
my $hashref = {A=>1, B=>{CC=>22, DD=>33}};
lock_keys(%{$hashref}); # this is OK
Hash::Util::lock_hash_recurse(%{$hashref}); # this fails: "Use of uninitialized value in string eq at /usr/lib/perl/5.10/Hash/Util.pm line 153."
From what I can tell, reftype returns undef... is that a bug in lock_hash_recurse (maybe that's why it isn't exported?...)
It is a bug in Hash::Util. The code says:
sub lock_hashref_recurse {
my $hash = shift;
lock_ref_keys($hash);
foreach my $value (values %$hash) {
if (reftype($value); eq 'HASH') {
lock_hashref_recurse($value);
}
Internals::SvREADONLY($value,1);
}
return $hash
}
but should be:
sub lock_hashref_recurse {
my $hash = shift;
lock_ref_keys($hash);
foreach my $value (values %$hash) {
my $type = reftype($value);
if (defined $type and $type eq 'HASH') {
lock_hashref_recurse($value);
}
Internals::SvREADONLY($value,1);
}
return $hash
}
The problem is that Scalar::Util::reftype returns undef, not an empty string. A patch has been sent to p5p. It doesn't look like Hash::Util is a dual-life (in core and CPAN) module, so you would have to upgrade to a version of Perl 5 with it fixed. I would suggest either patching the code yourself or writing your own version.
If you write your own version, do not use Internals::SvREADONLY (user level stuff shouldn't use the stuff in the Internals package). Use the Readonly::XS module instead.

Perl: if ( element in list )

I'm looking for presence of an element in a list.
In Python there is an in keyword and I would do something like:
if element in list:
doTask
Is there something equivalent in Perl without having to manually iterate through the entire list?
UPDATE:
The smartmatch family of features are now experimental
Smart match, added in v5.10.0 and significantly revised in v5.10.1, has been a regular point of complaint. Although there are a number of ways in which it is useful, it has also proven problematic and confusing for both users and implementors of Perl. There have been a number of proposals on how to best address the problem. It is clear that smartmatch is almost certainly either going to change or go away in the future. Relying on its current behavior is not recommended.
Warnings will now be issued when the parser sees ~~, given, or when.
If you can get away with requiring Perl v5.10, then you can use any of the following examples.
The smart match ~~ operator.
if( $element ~~ #list ){ ... }
if( $element ~~ [ 1, 2, 3 ] ){ ... }
You could also use the given/when construct. Which uses the smart match functionality internally.
given( $element ){
when( #list ){ ... }
}
You can also use a for loop as a "topicalizer" ( meaning it sets $_ ).
for( #elements ){
when( #list ){ ... }
}
One thing that will come out in Perl 5.12 is the ability to use the post-fix version of when. Which makes it even more like if and unless.
given( $element ){
... when #list;
}
If you have to be able to run on older versions of Perl, there still are several options.
You might think you can get away with using List::Util::first, but there are some edge conditions that make it problematic.
In this example it is fairly obvious that we want to successfully match against 0. Unfortunately this code will print failure every time.
use List::Util qw'first';
my $element = 0;
if( first { $element eq $_ } 0..9 ){
print "success\n";
} else {
print "failure\n";
}
You could check the return value of first for defined-ness, but that will fail if we actually want a match against undef to succeed.
You can safely use grep however.
if( grep { $element eq $_ } 0..9 ){ ... }
This is safe because grep gets called in a scalar context. Arrays return the number of elements when called in scalar context. So this will continue to work even if we try to match against undef.
You could use an enclosing for loop. Just make sure you call last, to exit out of the loop on a successful match. Otherwise you might end up running your code more than once.
for( #array ){
if( $element eq $_ ){
...
last;
}
}
You could put the for loop inside the condition of the if statement ...
if(
do{
my $match = 0;
for( #list ){
if( $element eq $_ ){
$match = 1;
last;
}
}
$match; # the return value of the do block
}
){
...
}
... but it might be more clear to put the for loop before the if statement.
my $match = 0;
for( #list ){
if( $_ eq $element ){
$match = 1;
last;
}
}
if( $match ){ ... }
If you're only matching against strings, you could also use a hash. This can speed up your program if #list is large and, you are going to match against %hash several times. Especially if #array doesn't change, because then you only have to load up %hash once.
my %hash = map { $_, 1 } #array;
if( $hash{ $element } ){ ... }
You could also make your own subroutine. This is one of the cases where it is useful to use prototypes.
sub in(&#){
local $_;
my $code = shift;
for( #_ ){ # sets $_
if( $code->() ){
return 1;
}
}
return 0;
}
if( in { $element eq $_ } #list ){ ... }
if( $element ~~ #list ){
do_task
}
~~ is the "smart match operator", and does more than just list membership detection.
grep is helpful here
if (grep { $_ eq $element } #list) {
....
}
If you plan to do this many times, you can trade-off space for lookup time:
#!/usr/bin/perl
use strict; use warnings;
my #array = qw( one ten twenty one );
my %lookup = map { $_ => undef } #array;
for my $element ( qw( one two three ) ) {
if ( exists $lookup{ $element }) {
print "$element\n";
}
}
assuming that the number of times the element appears in #array is not important and the contents of #array are simple scalars.
List::Util::first
$foo = first { ($_ && $_ eq "value" } #list; # first defined value in #list
Or for hand-rolling types:
my $is_in_list = 0;
foreach my $elem (#list) {
if ($elem && $elem eq $value_to_find) {
$is_in_list = 1;
last;
}
}
if ($is_in_list) {
...
A slightly different version MIGHT be somewhat faster on very long lists:
my $is_in_list = 0;
for (my $i = 0; i < scalar(#list); ++$i) {
if ($list[i] && $list[i] eq $value_to_find) {
$is_in_list = 1;
last;
}
}
if ($is_in_list) {
...
TIMTOWTDI
sub is (&#) {
my $test = shift;
$test->() and return 1 for #_;
0
}
sub in (#) {#_}
if( is {$_ eq "a"} in qw(d c b a) ) {
print "Welcome in perl!\n";
}
List::MoreUtils
On perl >= 5.10 the smart match operator is surely the easiest way, as many others have already said.
On older versions of perl, I would instead suggest List::MoreUtils::any.
List::MoreUtils is not a core module (some say it should be) but it's very popular and it's included in major perl distributions.
It has the following advantages:
it returns true/false (as Python's in does) and not the value of the element, as List::Util::first does (which makes it hard to test, as noted above);
unlike grep, it stops at the first element which passes the test (perl's smart match operator short circuits as well);
it works with any perl version (well, >= 5.00503 at least).
Here is an example which works with any searched (scalar) value, including undef:
use List::MoreUtils qw(any);
my $value = 'test'; # or any other scalar
my #array = (1, 2, undef, 'test', 5, 6);
no warnings 'uninitialized';
if ( any { $_ eq $value } #array ) {
print "$value present\n"
}
P.S.
(In production code it's better to narrow the scope of no warnings 'uninitialized').
Probably Perl6::Junction is the clearest way to do. No XS dependencies, no mess and no new perl version required.
use Perl6::Junction qw/ any /;
if (any(#grant) eq 'su') {
...
}
This blog post discusses the best answers to this question.
As a short summary, if you can install CPAN modules then the best solutions are:
if any(#ingredients) eq 'flour';
or
if #ingredients->contains('flour');
However, a more usual idiom is:
if #any { $_ eq 'flour' } #ingredients
which i find less clear.
But please don't use the first() function! It doesn't express the intent of your code at all. Don't use the "Smart match" operator: it is broken. And don't use grep() nor the solution with a hash: they iterate through the whole list. While any() will stop as soon as it finds your value.
Check out the blog post for more details.
PS: i'm answering for people who will have the same question in the future.
You can accomplish a similar enough syntax in Perl if you do some Autoload hacking.
Create a small package to handle the autoload:
package Autoloader;
use strict;
use warnings;
our $AUTOLOAD;
sub AUTOLOAD {
my $self = shift;
my ($method) = (split(/::/, $AUTOLOAD))[-1];
die "Object does not contain method '$method'" if not ref $self->{$method} eq 'CODE';
goto &{$self->{$method}};
}
1;
Then your other package or main script will contain a subroutine that returns the blessed object which gets handled by Autoload when its method attempts to be called.
sub element {
my $elem = shift;
my $sub = {
in => sub {
return if not $_[0];
# you could also implement this as any of the other suggested grep/first/any solutions already posted.
my %hash; #hash{#_} = ();
return (exists $hash{$elem}) ? 1 : ();
}
};
bless($sub, 'Autoloader');
}
This leaves you with usage looking like:
doTask if element('something')->in(#array);
If you reorganize the closure and its arguments, you can switch the syntax around the other way to make it look like this, which is a bit closer to the autobox style:
doTask if search(#array)->contains('something');
function to do that:
sub search {
my #arr = #_;
my $sub = {
contains => sub {
my $elem = shift or return;
my %hash; #hash{#arr} = ();
return (exists $hash{$elem}) ? 1 : ();
}
};
bless($sub, 'Autoloader');
}