Есть ли способ заменить if-elsif-else в Perl чем-то лучшим? - PullRequest
9 голосов
/ 30 ноября 2011

Я хочу создать группу подпрограмм Perl, которые имеют один и тот же шаблон if elsif elsif else, который принимает решение на основе факторной переменной.Вот пример шаблона подпрограммы:

sub get_age{

  my $factor=shift;

  if    ($factor == 1 ){ print "do something" }
  elsif ($factor == 2 ){ print "do somthing2" }
  elsif ($factor == 3 ){ print "do somthing3" }
  elsif ($factor == 4 ){ print "do somthing4" }
  else                 { print "error"        }
  }

Мне интересно, есть ли какой-нибудь шаблон проектирования на Perl для замены условия if else на более элегантное решение, которое легко поддерживать в будущем, особенно если мне нужно изменитьнекоторые условия или удалить некоторые из них?

Ответы [ 6 ]

10 голосов
/ 30 ноября 2011

Несколько человек упомянули таблицу отправки. Есть две вещи, и иногда приятно их разделять. Есть список возможных вещей, которые могут произойти, и то, что заставляет их происходить. Если вы соедините два, вы застряли с вашим решением. Если вы держите их отдельно, у вас будет больше гибкости позже.

Таблица диспетчеризации определяет поведение как данные, а не структуру программы. Вот два разных способа сделать это. В вашем примере у вас есть целые числа, и что-то подобное может использовать массив для хранения вещей. Пример хеша та же идея, но выглядит поведение немного по-другому.

Также обратите внимание, что я исключаю print. Когда вы повторили подобный код, попробуйте переместить повторяющийся материал на уровень.

use v5.10;

foreach my $factor ( map { int rand 5 } 0 .. 9 ) {
    say get_age_array( $factor );
    }

my @animals = qw( cat dog bird frog );
foreach my $factor ( map { $animals[ rand @animals ] } 0 .. 9 ) {
    say get_age_hash( $factor );
    }

sub get_age_array {
    my $factor = shift;

    state $dispatch = [
        sub { 'Nothing!' }, # index 0
        sub { "Calling 1" },
        sub { 1 + 1 },
        sub { "Called 3" },
        sub { time },
        ];

    return unless int $factor <= $#$dispatch;

    $dispatch->[$factor]->();   
    }


sub get_age_hash {
    my $factor = shift;

    state $dispatch = {
        'cat'  => sub { "Called cat" },
        'dog'  => sub { "Calling 1"  },
        'bird' => sub { "Calling 2, with extra" },
        };

    return unless exists $dispatch->{$factor};

    $dispatch->{$factor}->();   
    }
7 голосов
/ 30 ноября 2011

Обновление: Убедитесь, что вы прочитали комментарий Брайана ниже;в основном, лучше использовать for вместо given из-за различных проблем, которые он комментирует в своей ссылке.Я обновил свой совет, чтобы включить его улучшения, которые он обрисовал в общих чертах в Использовать для () вместо данного () :

Если вы используете Perl 5.10 или новее, given/whenэто волшебная пара, которую вы ищете, но вы должны использовать вместо нее for/when. Вот пример:

use strict;
use warnings;
use feature qw(switch say);

print 'Enter your grade: ';
chomp( my $grade = <> );

for ($grade) {
    when ('A') { say 'Well done!'       }
    when ('B') { say 'Try harder!'      }
    when ('C') { say 'You need help!!!' }
    default { say 'You are just making it up!' }
}
3 голосов
/ 30 ноября 2011

просто сделать вещи короче:

sub get_age1 {
    my $age = shift;
    $age == 1 ? print "do something" :
    $age == 2 ? print "do somthing2" :
    $age == 3 ? print "do somthing3" :
    $age == 4 ? print "do somthing4" :
                print "error"
}

это имеет смысл, если условие может быть лучше всего выражено в виде регулярного выражения:

sub get_age2 {    
    for (shift) { 
        if    (/^ 1 $/x) {print "do something"}
        elsif (/^ 2 $/x) {print "do somthing2"}
        elsif (/^ 3 $/x) {print "do somthing3"}
        elsif (/^ 4 $/x) {print "do somthing4"}
        else             {print "error"       }
    }
}

Вот несколько таблиц отправки:1007 *

простой (с ошибкой):

{
    my %age = ( # defined at runtime
        1 => sub {print "do something"},
        2 => sub {print "do somthing2"},
        3 => sub {print "do somthing3"},
        4 => sub {print "do somthing4"},
    );
    # unsafe to call get_age3() before sub definition
    sub get_age3 {
        ($age{$_[0]} or sub {print "error"})->()
    }
}

лучше:

{
    my %age;
    BEGIN {
        %age = ( # defined at compile time
            1 => sub {print "do something"},
            2 => sub {print "do somthing2"},
            3 => sub {print "do somthing3"},
            4 => sub {print "do somthing4"},
        )
    }
    # safe to call get_age4() before sub definition
    sub get_age4 {
        ($age{$_[0]} or sub {print "error"})->()
    }
}

другой способ написать это:

BEGIN {
    my %age = ( # defined at compile time
        1 => sub {print "do something"},
        2 => sub {print "do somthing2"},
        3 => sub {print "do somthing3"},
        4 => sub {print "do somthing4"},
    );
    # safe to call get_age5() before sub definition
    sub get_age5 {
        ($age{$_[0]} or sub {print "error"})->()
    }
}

еще один хороший способ написать это:

{
    my $age;
    # safe to call get_age6() before sub definition
    sub get_age6 {
        $age ||= { # defined once when first called
           1 => sub {print "do something"},
           2 => sub {print "do somthing2"},
           3 => sub {print "do somthing3"},
           4 => sub {print "do somthing4"},
        };
        ($$age{$_[0]} or sub {print "error"})->()
    }
}
0 голосов
/ 30 ноября 2011

Диспетчерские столы идеально подходят для этого типа шаблона проектирования.Я использовал эту идиому много раз.Примерно так:

sub get_age {
    my $facter = shift;
    my %lookup_map = (
        1 => sub {.....},
        2 => sub {.....},
        3 => \&some_other_sub,
        default => \&some_default_sub,
    );
    my $code_ref = $lookup_map{$facter} || $lookup_map{default};
    my $return_value = $code_ref->();
    return $return_value;
}

Это работает, когда аргумент, который вы используете для определения того, какой случай выполняется, будет существовать как ключ в вашей хеш-таблице.Если возможно, что это не будет точное совпадение, то вам может потребоваться использовать регулярные выражения или какой-либо другой способ сопоставления вашего ввода с тем, какой бит кода выполнять.Вы можете использовать регулярные выражения в качестве хеш-ключей, например:

my %patterns = (
    qr{^/this/one}i => sub {....},
    qr{^/that/one}is => sub {....},
    qr{some-other-match/\d+}i => \&some_other_match,
)
my $code_ref;
for my $regex (keys %patterns) {
    if ($facter =~ $regex) {
        $code_ref = $patterns{$regex};
        last;
    }
}
$code_ref ||= \&default_code_ref;
$code_ref->();
0 голосов
/ 30 ноября 2011

Это может быть место для чего-то вроде таблицы отправки.Я сам этого не делал, но эта страница может быть началом: http://www.perlmonks.org/?node_id=456530

0 голосов
/ 30 ноября 2011

использовать Переключатель ;

Чтение Таблицы рассылки in Perl высшего порядка .

...