Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery
 
PerlMonks  

sub as mathematical function

by Anonymous Monk
on Sep 04, 2019 at 11:23 UTC ( [id://11105573]=perlquestion: print w/replies, xml ) Need Help??

Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hi, can this sub be expressed in a simple mathematical function like int(8*$v) that returns exactly the same?
sub quant { # input: float 0 to 1 inclusive my $v = shift; if ($v<1/9) { return 0 } elsif ($v<2/9) { return 1 } elsif ($v<3/9) { return 2 } elsif ($v<4/9) { return 3 } elsif ($v<5/9) { return 4 } elsif ($v<6/9) { return 5 } elsif ($v<7/9) { return 6 } elsif ($v<8/9) { return 7 } else { return 8 } }

Replies are listed 'Best First'.
Re: sub as mathematical function
by LanX (Saint) on Sep 04, 2019 at 11:35 UTC
    Provided

    > input: float 0 to 1 inclusive

    It's  int(9*$v)

    Edit

    Corrected off by one

    Updated

    Corrected correction ;)

    Updated

    The edge case 1=9/9 needs special treatment. Are you sure 8 is the maximum?

    If yes max(8,int(9*$v))

    max needs to be imported. (POSIX?)

    List::Util#max

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

      min(int(9*$x),8) is perfect thanks all :-)
        Oh ... off by max, again! ;-)

        update

        I wouldn't count on correct input though, and additional max seems safe

        DB<5> print $_,":",max(min(int(9*$_/18),8),0),"\n" for -2..20 -2:0 -1:0 0:0 1:0 2:1 3:1 4:2 5:2 6:3 7:3 8:4 9:4 10:5 11:5 12:6 13:6 14:7 15:7 16:8 17:8 18:8 19:8 20:8

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

        >> min(int(9*$x),8)

        When number of intervals vary (not only 9), then this looks more general:
        my $intervals = 9; min( int( $intervals * $x ), $intervals - 1 )
        or:
        my $intervals = 9; int( $intervals * $x ) - int $x
Re: sub as mathematical function
by holli (Abbot) on Sep 04, 2019 at 11:38 UTC
    sub quant{ # 14 characters, but I am bad at this, I'm sure it can get shorter int((shift)*8) }
    Update:
    Hah. I made the same mistake as LanX above. It works up to 8/9, then it fails.

    sub quant{ int((shift)*8) } for ( 1 .. 9 ) { print "$_ => ", quant( ($_/9) - 0.0001 ), "\n"; }
    1 => 0 2 => 1 3 => 2 4 => 3 5 => 4 6 => 5 7 => 6 8 => 7 9 => 7 // BOOM!
    What do we learn? Test all cases :-)


    holli

    You can lead your users to water, but alas, you cannot drown them.

      It also fails for a lot of numbers between the ones you tested (e.g. 0.112 should return 1 but returns 0, 0.23 should return 2 but returns 1, etc).

      The fix is simple: Replace 8 with 9 :)

      Of course, that assumes the input is in range [0..1), but that seems a sure thing.

Re: sub as mathematical function
by talexb (Chancellor) on Sep 04, 2019 at 13:47 UTC

    This is a good example of a bad 'code smell'. If you find yourself doing a copy and paste for more than two lines .. you are probably doing it wrong. Also, a test script would have confirmed that this function had implemented the ideal behaviour.

    Alex / talexb / Toronto

    Thanks PJ. We owe you so much. Groklaw -- RIP -- 2003 to 2013.

Re: sub as mathematical function
by Corion (Patriarch) on Sep 04, 2019 at 11:26 UTC
      sorry I dont get what you're saying :-( I tried floor(8*$x), round(8*$x), ceil(8*$x) from the POSIX module but their all different from the sub

        I linked to the documentation of the modulo operator. Your subroutine basically seems to do:

        sub mod_9 { my( $x ) = @_; return $x % 9 }

        Update: No, I misread the original function. You seem to input a number with decimals and want to know into which 9-tile it falls. int ($x*9)-1, as LanX notes below, is the correct answer then.

Re: sub as mathematical function
by ikegami (Patriarch) on Sep 04, 2019 at 16:20 UTC

    Assuming $v is in [0..1)

    sub quant { my $v = shift; return int($v*9); }

      Measure (and cost) of returning "exactly the same" result unspecified, a pedant would note it's pure luck and coincidence that for 9 intervals these functions indeed seem to always return the same. For a simple case of 6 intervals:

      ~$ perl -E 'say 4 if 0.8333333333333333 < 5/6' 4 ~$ perl -E 'say int 6 * 0.8333333333333333' 5

      For maybe general case of 100 intervals, playing with Data::Float (and from similar experiment literal number above came from):

      ~$ perl -MData::Float=nextdown -E '$n=100; for(1..$n){say if $_-1 != i +nt $n*nextdown $_/$n}' 5 10 17 20 23 34 40 46 67 68 80 81 92 93

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11105573]
Approved by marto
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others scrutinizing the Monastery: (4)
As of 2024-04-25 06:38 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found