4

当社では、このコード (最後に記載) を約 10 年間使用していましたが、問題なく動作しました。

数日前、いくつかの問題に直面し、パッケージ全体を再コーディングする必要がありました。このコードを Damian の Switch モジュールに置き換えることにしました (コードの読みやすさを向上させるため)。

すべてがうまくいっています。

後でPerlmonksで、Damian がこのモジュールを下に置いていることを知りました。

Damian モジュールは、将来のコア言語機能の調査とプロトタイプ作成を目的としているため、本番環境で使用しないでください。

しかし、このモジュールの制限に達していないため、問題なく機能しています (推測します)。

ここで、両方の実装 (ネストされた if else と switch) を見ていただき、新しい実装で Switch を使用しても問題ないのか、それとも今後問題が発生するのか教えてください。以下のコードで Switch を使用しても問題ありませんか、それとも隠れたバグや問題はありますか?

私はすでに CPAN と Perlmonks でこのモジュールのバグとレビューを読んでおり、私たちのコードはそれらのバグに当たるにはほど遠いと思います (そう思う)。

を使用してPerl 5.8.5います。

PS:私は Switch の代替手段を知っていますgiven/when。Perl 5.10 にはあります。ここdispatch tableで指定されている他のソリューションを使用できますが、今は、Switch を使用する新しい実装を比較したいだけです。

ネストされた if else の使用

if ($command =~ /^enter$/) {
    $self->show_main_frames();
}       
elsif ($command =~ /^XYZ_MENU/i) {
    $self->show_main_menu($manual, $dbot);
}
elsif ($command =~ /^DBOT/i) {
    $dbot->process();
}
# XML is used for the reminders-history: Request 2666
elsif ($command =~ /^XML_DBOT/i) {
    $dbot->process();
}
elsif ($command =~ /^UGS/i) {
    $ugsui->process();
}
elsif ($command eq "kill") {
    my $login = $self->{COMMON_HASH}{login} || "";
    my $su_login = $self->{CONF}->get("start", "SU_LOGIN");
    if ($login eq $su_login) {      
            # usually only certain user with certain permission will be
            # able to do this. 
            $self->do_error("Daemon was killed by ".$login);
            $self->db_connection->disconnect();
            $self->{LOG}->write("User $login killed the daemon", 0);
            exit; # this 'exit' actually kill the daemon
    }
    else {
            $self->do_error("User $login tried to kill the daemon. ".
            "This incident will be reported");
            $self->{LOG}->write("User $login tried to kill the daemon", 2);
    }
}
elsif ($command eq "logout") {
    # check if we should delete the password cookie
    my $forget_me = $self->{CGI}->param("forget_me") || 0;
    if ($forget_me) {
            $self->{DB_PASSWORD_COOKIE}->delete_cookie();
    }

    $ugsui->do_logout();
    # Cliff edit remove id from logged_in
    $session->remove_session($session->login());
    # delete the session of the user
    delete $self->{SESSIONS}{$session->id()};
    if ($self->{CACHE_TO_FILE}) {
            my $session_data_path = 
                XYZ_DIR
            ."/code/cache/session_data"
        .$session->id();
            unlink($session_data_path);
    }
}
# if we just login we should create all the main frames        
elsif ($command eq "login") {
    # if extra_param holds "command*XXX" the XXX will be placed instead of
    # the command. extra_param holds pairs that are astrics-separated
    my $extra_param = $cgi->param("extra_param");
    $extra_param = "" if (!defined($extra_param));
    $extra_param =~ /command\*([^\*]+)/i;
    my $other_command = defined($1) ? $1 : "";
    if ($other_command =~ /^dbot/i) { # meanwhile - works only on dbot 
                                  # commands
        $command = $other_command;
            # now we will get the other parameters from the extra_param 
            # (actually including the command that is still in the 
            # $extra_param)         
            while ($extra_param =~ /^\*?([^\*]+)\*([^\*]+)(.*)/) {
            $extra_param = $3;
            my $name = $1;
            my $value = $2;     
            $cgi->param(-name => $name,
                 -value => $value);
            }#end while
    }#end if
    else{
    $self->show_main_frames();
    }
}#end elsif
else {
    $self->show_main_frames();
}#end outer else

スイッチの使用

switch ($command) 
{
    case /^enter$/      { $self->show_main_frames() }
    case /^XYZ_MENU/i   { $self->show_main_menu($manual, $dbot) }
    case /^DBOT/i       { $dbot->process() }
        case /^XML_DBOT/i   { $dbot->process() }
        case /^UGS/i        { $ugsui->process() }
        case "kill"     {
                my $login = $self->{COMMON_HASH}{login} || "";
                    my $su_login = $self->{CONF}->get("start", "SU_LOGIN");
                if ($login eq $su_login) {      
                        # usually only certain user with certain permission will be
                        # able to do this. 
                        $self->do_error("Daemon was killed by ".$login);
                        $self->db_connection->disconnect();
                        $self->{LOG}->write("User $login killed the daemon", 0);
                        exit; # this 'exit' actually kill the daemon
                    }
                else    {
                        $self->do_error("User $login tried to kill the daemon. ".
                            "This incident will be reported");
                        $self->{LOG}->write("User $login tried to kill the daemon", 2);
                    }
                    }
        case "logout"       {
                    # check if we should delete the password cookie
                    my $forget_me = $self->{CGI}->param("forget_me") || 0;
                    if ($forget_me) {
                            $self->{DB_PASSWORD_COOKIE}->delete_cookie();
                    }

                $ugsui->do_logout();
                # Cliff edit remove id from logged_in
                $session->remove_session($session->login());
                # delete the session of the user
                delete $self->{SESSIONS}{$session->id()};
                    if ($self->{CACHE_TO_FILE}) {
                            my $session_data_path = 
                                XYZ_DIR
                        ."/code/cache/session_data"
                        .$session->id();
                    unlink($session_data_path);
                    }
                    }
        case "login"        {
                # if extra_param holds "command*XXX" the XXX will be placed instead of
                # the command. extra_param holds pairs that are astrics-separated
                my $extra_param = $cgi->param("extra_param");
                $extra_param = "" if (!defined($extra_param));
                $extra_param =~ /command\*([^\*]+)/i;
                my $other_command = defined($1) ? $1 : "";
                if ($other_command =~ /^dbot/i) 
                    { # meanwhile - works only on dbot 
                                       # commands
                    $command = $other_command;
                        # now we will get the other parameters from the extra_param 
                        # (actually including the command that is still in the 
                        # $extra_param)         
                        while ($extra_param =~ /^\*?([^\*]+)\*([^\*]+)(.*)/) {
                        $extra_param = $3;
                        my $name = $1;
                        my $value = $2;     
                        $cgi->param(-name => $name,
                             -value => $value);
                            }#end while
                    }#end if
                else {$self->show_main_frames();}
                }
    else            {$self->show_main_frames();}
} # end switch
4

3 に答える 3

8

Switchは、独自のソースコードの解析を行います。これは、それを直接使用するコードのエラーを診断するのが困難になる可能性があります。Switchが作成する問題の種類は断続的ではないため、コードが機能する場合は、心配する必要はありません。

しかし、実際には、それはまったく追加しません。

スイッチ付き:

switch ($command) {
    case /^enter$/      { $self->show_main_frames() }
    case /^XYZ_MENU/i   { $self->show_main_menu($manual, $dbot) }
    case /^DBOT/i       { $dbot->process() }
    case /^XML_DBOT/i   { $dbot->process() }
    case /^UGS/i        { $ugsui->process() }
    case "kill"         {
        my $login = $self->{COMMON_HASH}{login} || "";

スイッチなし:

for ($command) {
    if    (/^enter$/)      { $self->show_main_frames() }
    elsif (/^XYZ_MENU/i)   { $self->show_main_menu($manual, $dbot) }
    elsif (/^DBOT/i)       { $dbot->process() }
    elsif (/^XML_DBOT/i)   { $dbot->process() }
    elsif (/^UGS/i)        { $ugsui->process() }
    elsif ($_ eq "kill")   {
        my $login = $self->{COMMON_HASH}{login} || "";

elsif (/^kill\z/)これも機能します。)

于 2012-11-22T06:07:06.870 に答える
7

実際、 Switchモジュールは「キラー機能」を提供しません。安全で安定しており、スイッチのような欠点がないelsifステートメントでも同じことができます。これが私のプロジェクトで得たスイッチの問題です(そして私はもう使用しません):

切り替えは、Perl フィルターを介して行われます。この手法には次の制限があります。

  • ソースコードは実際にその場で書き直され、一連のelsifステートメントに置き換えられます。
  • 一部の Perl エラー レポートは間違った行を参照します。ソースにないコード (自動生成されたコード) を示すものもあります。

フィルター制限ではなく、モジュール自体の制限:

  • 呼び出し先のファイル (.pl または .pm) がSwtich を使用して 1Mbyte を超えるサイズの場合、「謎のエラー」が発生する可能性があります (ドキュメントに記載されています)。これらのエラーがSwitchモジュールにつながるものではなく、完全に自明であることを確認できます。そのため、数週間のコーディング/ドキュメントの後でデバッグに苦労する可能性があります。

Perl 5.10 以降で使用できるelsifまたはステートメントを使用することをお勧めします。given..whenしたがって、perl 5.8.x を使用している場合は、elsif を使用してください。

また、スイッチのドキュメントの「制限事項」の段落を読むこともできます。

于 2012-11-22T06:36:22.833 に答える
4

Switch は独自のソース コード解析を行うため、特定の状況ではまったく機能しません。たとえば、mod_perl では使用できません。

ただし、Perl 5.10 以降を使用している場合は、実質的に同じ機能を備えたより優れた代替手段があります: given/when

use v5.10;

given ($var) {
when (/^abc/) { $abc = 1 }
when (/^def/) { $def = 1 }
when (/^xyz/) { $xyz = 1 }
default       { $nothing = 1 }
}

givenは Perl コアでサポートされています (そして mod_perl を含むどこでも動作します) -use v5.10;すぐに利用できます。

于 2012-11-22T06:35:40.490 に答える