Popcorn Dave has asked for the wisdom of the Perl Monks concerning the following question:

Fellow monks,

I'm doing a conversion of some pascal code of a friend's to Perl to convince him to learn Perl. And after seeing his 8 pages of code I was sorely convinced I could do it in no more than 2 in Perl and still make it non-obsfu. :)

Given the following code in pascal:

function roll(point : integer) : real; begin case point of 2,12: roll := 1/36; 3,11: roll := 2/36; 4,10: roll := 3/36; 5,9: roll := 4/36; 6,8: roll := 5/36; 7: roll := 6/36; else writeln('invalid roll'); roll := 0; end; { case } end; { rolls }

This is what I've converted it to in Perl:

sub roll{ my $i = shift; my %roll=( 2=>1, 3=>2,4=>3, 5=>4, 6=>5, 7=>6, 8=>5, 9=>4, 10=>3, 11=>2, 12=>1); $i = $roll{$i}/36; return $i; }

What I'm curious about is this: Is there a way to do this cleaner or more efficiently? At first I thought tr was the way to go until I realized it was only going to work for 2-9, and once you hit 10-12 you were stuck.

I also thought about the array approach using just the indicies 2-12 but the hash just lays it out so well.

Any ideas would be greatly appreciated!

Some people fall from grace. I prefer a running start...

Replies are listed 'Best First'.
Re: Converting pascal code to perl
by YuckFoo (Abbot) on Aug 15, 2002 at 04:01 UTC
    I think it's clear but others might find it too cute:

    if ($point >= 2 && $point <= 12) { return (6 - abs(7 - $point)) / 36; }
    CrapsFoo
      That's one I hadn't even thought of. Thanks!

      I do think it's one of the pitfalls you run in to as you get better in perl is trying to do things that "aren't cute" but function just fine.

      Some people fall from grace. I prefer a running start...

Re: Converting pascal code to perl
by spurperl (Priest) on Aug 15, 2002 at 06:26 UTC
    IMHO, you guys are not taking a good approach to "convince his friend to use Perl". Point is that you try to 'golf' a pretty simple function, which causes it to lose readability, and that is very bad when maintenance is needed.

    Surely, things can be made shorter in Perl than in Pascal, but shorter in places when Perl really has an advantage, like regexes, list processing and hashes (and many more, of course). I'm in no doubt that a Golf guru can knock the friend's whole 8-page program into a 512 byte block, but that is hardly the point.

    On the contrary, you should show the friend how Perl can make things more clear and natural, and win where complex operations (which need to be implemented in Pascal) are used.

    Good luck and happy Perl-ing

      Actually that's my fault for not describing the whole problem in my original post.

      The original program takes data from web pages and parses the data to do a simluation on baseball players for a fantasy baseball league. My thought was, after looking at his pascal code and the hoops he had to jump through to do the parsing, that we could both combine the culling of the information and the process of the simulation in to one program.

      My thought was that with the text parsing capabilities Perl has, it was hands down the obvious language choice for this exercise. Of course he went ahead and did his in pascal anyway as he doesn't as of yet know Perl, so I thought I would convert it ( and him ) and see how much easier I could do it and sharpen my chops in the process.

      Some people fall from grace. I prefer a running start...

Re: Converting pascal code to perl
by BrowserUk (Patriarch) on Aug 15, 2002 at 04:45 UTC

    Pipped to the post on the anon array solution, if you really want to retain the semantics of the hash:

    #! perl -w use strict; sub roll { ( {2=>1, 3=>2, 4=>3, 5=>4, 6=>5, 7=>6, 8=>5, 9=>4, 10=>3, 11=>2, +12=>1}->{ shift() || 0 } ) / 36 or warn "Invalid roll\n" and 0; } print roll( $_),$/ for (2..12); __END__ C:\test>190287 0.0277777777777778 0.0555555555555556 0.0833333333333333 0.111111111111111 0.138888888888889 0.166666666666667 0.138888888888889 0.111111111111111 0.0833333333333333 0.0555555555555556 0.0277777777777778

    What's this about a "crooked mitre"? I'm good at woodwork!
Re: Converting pascal code to perl
by Anonymous Monk on Aug 15, 2002 at 03:47 UTC
    sub roll { ((0,0,1,2,3,4,5,6,5,4,3,2,1)[shift]||0)/36 or warn "invalid roll" and 0; }
Re: Converting pascal code to perl
by Abigail-II (Bishop) on Aug 15, 2002 at 09:42 UTC
    You know, I find the Pascal code far clearer than your Perl code. One glance at the Pascal code, and it's immediate clear to me what it does, it returns the chance of rolling the given number with two dice.

    I don't get that from the Perl code - the symmetry of the case statement is lost in the hash.

    Abigail

      This version is not any shorter than the Pascal version, but perhaps nearly as clear to someone that understands perl regexes.
      use strict; use warnings; sub roll { my $point = int($_[0]); # Just to be safe $point =~ /^(?:2|12)$/ && return 1/36; $point =~ /^(?:3|11)$/ && return 2/36; $point =~ /^(?:4|10)$/ && return 3/36; $point =~ /^(?:5|9)$/ && return 4/36; $point =~ /^(?:6|8)$/ && return 5/36; $point == 7 && return 6/36; warn "Invalid roll"; return 0; }
Re: Converting pascal code to perl
by Necos (Friar) on Aug 15, 2002 at 09:39 UTC
    How about we combine all of the answers given and try this:
    # perl -w; use strict; sub roll1 { return ((0,1,2,3,4,5,6)[abs(6-abs($_[0]-7))])/36; } print roll1($_),$/ for (2..12);
    I checked this out on my Win32 machine and it produces:

    C:\>perl pascal.pl
    0.0277777777777778
    0.0555555555555556
    0.0833333333333333
    0.111111111111111
    0.138888888888889
    0.166666666666667
    0.138888888888889
    0.111111111111111
    0.0833333333333333
    0.0555555555555556
    0.0277777777777778

    just like everyone elses code. Hope that helps some...

    Theodore Charles III
    Network Administrator
    Los Angeles Senior High
    4650 W. Olympic Blvd.
    Los Angeles, CA 90019
    323-937-3210 ext. 224
    email->secon_kun@hotmail.com
    perl -e "map{print++$_}split//,Mdbnr;"
Re: Converting pascal code to perl
by greenFox (Vicar) on Aug 16, 2002 at 03:33 UTC
    Well I would go for a different solution to all the others here which all rely on you having pre-calculated the dice scores, OK if you are working with d6 but what if you go to d8 or d12? (giving away my AD&D heritage here :)

    for (1..13){ my $chance = chance_of_2dice_score($_, 6); print "Chances of rolling a $_ on two dice is: $chance\n"; } sub chance_of_2dice_score { my $score = shift; my $dice = shift; my %valid_dice = map {$_,1} (4,6,8,10,12,20); die "Invalid dice $dice" if (! $valid_dice{$dice}); my $count = 0; for my $first (1..$dice){ for my $second (1..$dice){ ++$count if (($first + $second) == $score); } } return ($count / ($dice * $dice)); }

    I think that this is clearer than the other solutions because I can see the mechanics of it. Now, does any-one have an algorithm for x dice? :-)

    --
    Until you've lost your reputation, you never realize what a burden it was or what freedom really is. -Margaret Mitchell