Having solved this second excersise, I returned to the first. That one looked like it’d need more code.

Write a script that computes the equal point in the Fahrenheit and Celsius scales, knowing that the freezing point of water is 32 °F and 0 °C, and that the boiling point of water is 212 °F and 100 °C.

I guess you could explain equal points in many ways. I think of it as the point where two lines cross. In this case: What’s the point where the celcius # and the fahrenheit # is identical and indicates the same absolute temperature?

Before we begin: The answer is 40 below zero (-40). But how do we compute this easily? We have to use the formula for conversion. If x represents the temperature in celcius, fahrenheit — y — is calculated like this:

y = 9/5*x + 32

When you look at it like this you’ll see that this looks exactly like a formula for a line, the y = mx + b we know from school — where m is the gradient or the slope of the line and b the y intercept, i.e. the value of y when x = 0. In this case m = 9/5 and b = 32.

The celcius scale is easy — y is equal to x always, so the formula looks like this:

y = x

To calculate the equal point we have to find the value of x where y is the same for both formulas. We can build the equation for solving this by taking the right sides of the equation for fahrenheit and celcius lines and put them together like this…

(the right hand side of y = x) = (the right hand side of y = 9/5 * x + 32)

x = 9/5 * x + 32

…and solve the equation. Since 9/5 is 1.8, let’s use that instead. So…

x = 1.8x + 32

x-1.8x = 32 # or 1.0x-1.8x = 32

-0.8x = 32 # -0,8x because 1.0–1,8

x = 32 / -0.8

x = -40

Now we know enough to not only solve a comparision between celcius and fahrenheit, but between celcius and whatever scale you’d throw at it. We have to implement x = 32 / -0.8 in some way. Or, figure out a way to make -0.8 generic, i.e. put in any scale and get this to work.

Here’s the key: the grade 9/5 or 1.8 is really just the fahrenheit degrees for the boiling point of water subtracted by the same for the freezing point, and then divided by the same for celcius (which happens to be 100). I.e. (212-32)/100 = 9/5 = 1.8. So if we have the freezing and the boiling point of any temperature scale other than celcius itself, the grade for that scale is:

(freezing point-boiling point) / 100

Now, if we peek at on of the steps of the calculation above, 1.0x-1.8x = 32, we see that vi have to subtract the grade from 1.0, so that we end up with the final

y-intercept (or freezing point) / 1.0-grade

…or more verbosely

freezing point / (1.0-((freezing point-boiling point) / 100))

If we assume that we have a variable $a for the freezing point and a variable $b for the boiling point, a Perl 6 one-liner would look like this:

-> $a, $b { say "Equal point: " ~ ( $b - $a == 100 ?? "None" !! $a / (1 - (($b - $a) / 100))) }(32,212);

Substitute 32, 212 with anything you’d like. Let’s say you invented a temperature scale where 20 is freezing and 80 is boiling, swap (32,212) with (20,80). The result? 50.

Quick and dirty — but effective. However the code above will crash under certain circumstances, and in many scenarios calculate that the equal point is at a temperature that simply does not exists (temperatures below absolute zero). So I thought that I’d implement this with a little error handling as well. And I think that version showcase some Perl 6 greatness that I haven’t touched upon earlier:

File: eqpoint.p6

Run: ./eqpoint.p6 32 212

./eqpoint.p6 fahrenheit .................... #!/usr/bin/env perl6 multi MAIN( #= Compares the C. scale to any scale you dream up

Real $f, #= freezing point in custom scale

Real $b #= boiling point in custom scale

) {

say "There is no equal point for this scale." and exit if $b - $f == 100;

my $equal-point = $f / (1 - (($b - $f) / 100));

say "The calculated equal point is only theoretical as it is below absolute zero." if $equal-point < -273.15;

say "Equal point: $equal-point";

} multi MAIN( #= Compares the C. scale to a named temperature scale

Str $scale where { $_ ~~ m:i/^(fahrenheit|kelvin|rankin)$/ } #= Name of scale (Fahrenheit, Kelvin or Rankin)

) {

given $scale.fc {

when "fahrenheit" { MAIN( 32 , 212 ); }

when "kelvin" { MAIN(273.15, 373.15); }

when "rankin" { MAIN(491.67, 671.67); }

}

}

The use of the MAIN subroutines gives us some command line parsing for free. Not only that, but Perl 6 does type checking for us — in the case of the first multi MAIN, it ensures that the two parameters it receives actually are numbers. The second multi MAIN looks only for a single string, and only three strings in particular: fahrenheit, kelvin and rankin.

The program won’t run if you try to start it with any other parameters. Instead it spits out a Usage text like this:

Usage:

eqpoint.p6 <f> <b> -- Compares the Celcius scale to whatever scale you dream up

eqpoint.p6 <scale> -- Compares the Celcius scale to a given temperature scale



<f> freezing point in this scale

<b> boiling point in this scale

<scale> Name of scale (Fahrenheit, Kelvin or Rankin)

You’ll see that this custom usage text is caused by my use of #= in the code above. #= behind the sub routine name is the generic description of the usage. Behind parameter variables they describe that particular parameter.

Now, when calculating the grade I do some value checking to avoid certain scenarios. First I check whether the difference between boiling and freezing in the user’s custom sale equals 100. If it does, that scale runs in parallell with the celcius scale. That means that the lines never cross and there is no equal point. If that’s the case I print out a message about that and exit. (The equation would have cause a divide by zero error had I move on)

In addition I check whether the calulcated equation point is below -273.15 celcius. If it is, the point is below absolute zero and would never ever occur in this universe. In those scenarios I point out that the equal point is only theoretical.