How can I create a Perl subroutine that accepts more than one block?

倖福魔咒の 提交于 2020-05-09 01:15:46

问题


With prototypes, you can create a subroutine that accepts a block of code as its first parameter:

sub example (&) {
   my $code_ref = shift;
   $code_ref->();
}
example { print "Hello\n" };

How can I do the same thing, but with more than one block of code? I want to use blocks of codes, not variables or sub { ... }.

This does not work:

sub example2 (&&) {
   my $code_ref = shift;
   my $code_ref2 = shift;
   $code_ref->();
   $code_ref2->();
}
example2 { print "One\n" } { print "Hello\n" };

It gives this error:

Not enough arguments for main::example2

回答1:


I hope you realise that this is just code seasoning, and all you are achieving is a tidier syntax at the expense of clarity?

Perl won't allow you to pass more than one bare block to a subroutine, but the second actual parameter could be a call to a subroutine that also takes a single block and simply returns the code reference.

This program demonstrates. Note that I have chosen please and also as names for the subroutines. But you must use something that is both appropriate to your own code's functionality and very unlikely to clash with forthcoming extensions to the core language.

use strict;
use warnings;

sub please(&$) {
  my ($code1, $code2) = @_;
  $code1->();
  $code2->();
}

sub also(&) {
  $_[0];
}

please { print "aaa\n" } also { print "bbb\n" };

output

aaa
bbb



回答2:


works for me...

sub example2  {
   my $code_ref = shift;
   my $code_ref2 = shift;
   $code_ref->();
   $code_ref2->();
}
example2 ( sub { print "One\n" }, sub { print "Hello\n" });

Just for the purposes of TMTOWTDI here is a method that works in somewhat the way that the OP requested

First, make a source filter

package poop;

use Filter::Util::Call;

sub import {
    my ($type) = @_;
    my ($ref)  = [];
    filter_add( bless $ref );
}

sub filter {
    my ($self) = @_;
    my ($status);
if (( $status = filter_read() ) > 0) { 
        if(!/sub/ && /example2/) {
            s/\{/sub \{/g;
            }
        }

    $status;
}
1;

Second the filter must be used

use poop;

sub example2  {
   my $code_ref = shift;
   my $code_ref2 = shift;
   $code_ref->();
   $code_ref2->();
}
example2 ( { print "One\n" }, { print "Hello\n" });

ps. this is horrific and noone would wish to see this actually in production




回答3:


You could just eval the blocks

use experimental 'signatures';
use feature 'say'; 

sub doublethecode ($block1, $block2) { eval { $block1; }; eval { $block2;} } ;
doublethecode \&{ print "OH " }, \&{ say for qw/HAI YOU LOLCAT :-P/  }; 

output:

OH, HAI
YOU
LOLCAT
:-P

Then just ignore the \& and you're there.

As of v5.20 You can use signatures and :prototype attributes together. The signatures feature gives a more "common" syntax and a minimalist sort of argument checking. Maybe one day a future perl will have a "builtin" (optional, highly flexible) type system of some kind to go with it, but prototypes aren't anything like that right now. So something like:

sub doublethecode :prototype(\&\&) ($cr1, $cr2) { $cr1 ; $cr2 ; }

is not what it seems. Since prototypes, signatures and signatures with :prototype(&&) may not be giving you what think you're getting, this might be good enough:

sub doublethecode { eval { shift };  eval{ shift } } ;
doublethecode &{ print "perl" }, &{ print "6" }

output:

perl6

To make sure perl doesn't think {} is a hash, the & is necessary. But actually ... what is wrong with using anonymous subroutines sub { } here?




回答4:


Fwiw, in Perl 6:

sub doublecode (&foo, &bar) { foo(), bar() }
doublecode { print 1 }, { print 2 } # prints 12

But see my partial list of caveats about Perl 6.




回答5:


This works for me:

     example { print "One\n" } sub { print "Hello\n" };


来源:https://stackoverflow.com/questions/27044986/how-can-i-create-a-perl-subroutine-that-accepts-more-than-one-block

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!