Suman
Suman

Reputation: 3545

Exit the function without aborting the script

I have a function that accepts an array of positive values only. Dies when any negative value is given.

# Consider edge cases
# First check if all elements are > 0, if not throw informative error
# Then if any of element is zero return 0
# Only else proceed further towards calculation


sub geometric_mean(@data) {
    if @data.any < 0 {die "Not possible to calculate geometric mean for negative values"};
    if @data.any == 0 { return 0 };
    my $sum;
    for @data {
        $sum += log($_);
    }
    return exp($sum/@data.elems)    
}

With this if I try to run the script:

my @data = [1, 2, 3.8, -1];
say geometric_mean(@data);

say "Hello world";

the code exits at the function call without running the last line i.e does not run the codes down the function:

Not possible to calculate geometric mean for negative values
  in method throw at 'SETTING::'src/core.c/Exception.rakumod line 65
  in sub die at 'SETTING::'src/core.c/control.rakumod line 255
  in sub geometric_mean at c:\Users\suman\sum.p6 line 8
  in block <unit> at c:\Users\suman\sum.p6 line 20

The modification:

sub geometric_mean(@data) {
    if @data.any < 0 {die "Not possible to calculate geometric mean for negative values"};
    CATCH { default { warn "    Not possible to calculate geometric mean for negative values"; } };
    if @data.any == 0 { return 0 };
    my $sum;
    for @data {
        $sum += log($_);
    }
    return exp($sum/@data.elems)    
}

now runs the last line too with output:

    Not possible to calculate geometric mean for negative values
Nil
  in sub warn at 'SETTING::'src/core.c/control.rakumod line 269
Hello world

How to handle such cases? I looked at try CATCH blocks but that would execute the code below those blocks within the function which I want to avoid.

I went through this:

  1. Succinct way to change thrown Exception to Failure?
  2. https://docs.raku.org/routine/throw
  3. https://docs.raku.org/type/Exception
  4. https://dev.to/lizmat/exceptions-3g0i

Upvotes: 8

Views: 204

Answers (2)

librasteve
librasteve

Reputation: 7571

I think you are asking "how can I avoid the backtrace details emitted on a fail or die??

If so, you can just not generate an exception, like this:

sub geometric_mean(@data) {
    if @data.any < 0 { return "Not possible to calculate geometric mean for negative values" }
    if @data.any == 0 { return 0 };
    my $sum;
    for @data {
        $sum += log($_);
    }   
    exp($sum/@data.elems)               
}

my @data = [1, 2, 3.8, -1];
say geometric_mean(@data);

say "Hello world";
Not possible to calculate geometric mean for negative values
Hello world

EDIT: removed my speculative speculative try/CATCH variant since per comment not good to use try and CATCH in the same block.

Upvotes: 3

Hovercouch
Hovercouch

Reputation: 2314

die is for fatal exceptions. If you want to continue control flow, you don't want a fatal exception.

There are two ways you could fix this. First, use fail instead of die, and then check if the call failed with a with:

sub geometric_mean(@data) {
    if @data.any < 0 {fail  "Not possible to calculate geometric mean for negative values"};
// etc
}

my @data = [1, 2, 3.8, -1];
with geometric_mean(@data) -> {} else {say .exception.message};

say "Hello world";

The other way is to not throw an exception at all, and instead return:

sub geometric_mean(@data) {
    if @data.any < 0 {say  "Not possible to calculate geometric mean for negative values"; return};
// etc
}

my @data = [1, 2, 3.8, -1];
geometric_mean(@data);

say "Hello world";

Upvotes: 6

Related Questions