Perl程序返回两个堆栈调用级别

时间:2013-08-19 12:03:10

标签: perl procedural-programming

我的问题类似于: Is it possible for a Perl subroutine to force its caller to return? 但我需要程序方法。

我想用return来编写一些消息过程,示例基本代码:

sub PrintMessage {
    #this function can print to the screen and both to logfile
    print "Script message: $_[0]\n";
}

sub ReturnMessage {
    PrintMessage($_[0]);
    return $_[2];  #  <-- we thinking about *this* return
}

sub WorkingProc {
    PrintMessage("Job is started now");
    #some code
    PrintMessage("processed 5 items");

    # this should return from WorkingProc with given exitcode
    ReturnMessage("too many items!",5) if $items>100;

    #another code
    ReturnMessage("time exceded!",6) if $timespent>3600;
    PrintMessage("All processed succesfully");
    return 0;
}

my $ExitCode=WorkingProc();
#finish something
exit $ExitCode

想法是,如何使用ReturnMessage函数内部的返回来使用WorkingProc函数中的指定代码退出?注意,从许多地方调用ReturnMessage函数。

3 个答案:

答案 0 :(得分:3)

这是不可能的。但是,您可以明确返回:

sub WorkingProc {
    PrintMessage("Job is started now");
    ...
    PrintMessage("processed 5 items");

    # this returns from WorkingProc with given exitcode
    return ReturnMessage("to much items!", 5) if $items > 100;

    ...
    return ReturnMessage("time exceded!", 6) if $timespent > 3600;
    PrintMessage("All processed succesfully");
    return 0;
}

一个sub可以有任意数量的return语句,所以这不是问题。

这种解决方案优于黑客通过调用堆栈,因为控制流程对读者来说更为明显。你梦寐以求的是一种GOTO,大多数人不写C或BASIC等已经放弃了45年前。

您的代码依赖退出代码来确定子例程中的错误。 *叹*。 Perl有一个相当倒退的异常系统,但仍然比 更先进。

die "Reason"use Carpcroak "Reason"犯了致命错误。使用Try::TinyTryCatch模块捕获错误。

sub WorkingProc {
    PrintMessage("Job is started now");
    ...
    PrintMessage("processed 5 items");

    # this should return from WorkingProc with given exitcode
    die "Too much items!" if $items > 100;

    ...
    die "Time exceeded" if $timespent > 3600;
    PrintMessage("All processed succesfully");
    return 0;
}

WorkingProc();

如果抛出错误,则会以非零状态退出。

答案 1 :(得分:2)

为非本地返回而想到的方法是从最里面的函数抛出异常(die)。

然后你需要有一些包装代码来处理它在顶层。您可以设计一组实用程序例程来自动设置它。

答案 2 :(得分:2)

Log::AnyLog::Any::AdapterException::Class结合使用,您可以将所有部分放在一起,而且操作简单,灵活性最高:

#!/usr/bin/env perl

package My::Worker;
use strict; use warnings;

use Const::Fast;
use Log::Any qw($log);

use Exception::Class (
    JobException => { fields => [qw( exit_code )] },
        TooManyItemsException => {
            isa => 'JobException',
            description => 'The worker was given too many items to process',
        },
        TimeExceededException => {
            isa => 'JobException',
            description => 'The worker spent too much time processing items',
        },
);

sub work {
    my $jobid = shift;
    my $items = shift;

    const my $ITEM_LIMIT => 100;
    const my $TIME_LIMIT => 10;

    $log->infof('Job %s started', $jobid);

    shift @$items for 1 .. 5;
    $log->info('Processed 5 items');

    if (0.25 > rand) {
        # throw this one with 25% probability
        if (@$items > $ITEM_LIMIT) {
            TooManyItemsException->throw(
                error => sprintf(
                    '%d items remain. Limit is %d.',
                    scalar @$items, $ITEM_LIMIT,
                ),
                exit_code => 5,
            );
        }
    }

    { # simulate some work that might take more than 10 seconds
        local $| = 1;
        for (1 .. 40) {
            sleep 1 if 0.3 > rand;
            print '.';
        }
        print "\n";
    }
    my $time_spent = time - $^T;
    ($time_spent > $TIME_LIMIT) and
        TimeExceededException->throw(
            error => sprintf (
                'Spent %d seconds. Limit is %d.',
                $time_spent, $TIME_LIMIT,
            ),
            exit_code => 6);
    $log->info('All processed succesfully');
    return;
}

package main;

use strict; use warnings;
use Log::Any qw( $log );
use Log::Any::Adapter ('Stderr');

eval { My::Worker::work(exceptional_job => [1 .. 200]) };
if (my $x = JobException->caught) {
    $log->error($x->description);
    $log->error($x->error);
    exit $x->exit_code;
}

示例输出:

Job exceptional_job started
Processed 5 items
........................................
The worker spent too much time processing items
Spent 12 seconds. Limit is 10.

Job exceptional_job started
Processed 5 items
The worker was given too many items to process
195 items remain. Limit is 100.