RogerBW's Blog

Perl Weekly Challenge 120: Swap Clock 07 July 2021

I’ve been doing the Perl Weekly Challenges. The latest involved more binary manipulation and analogue clocks. (Note that this is open until 11 July 2021.)

TASK #1 › Swap Odd/Even bits

You are given a positive integer $N less than or equal to 255.

Write a script to swap the odd positioned bit with even positioned bit and print the decimal equivalent of the new binary representation.

As with last week, one could do this by converting to a binary string, but we already have bitwise operators so we might as well use them. (If there were no limit on the input size it might be a bit more fiddly.)

  return (($n & 0x55)<<1) | (($n & 0xAA)>>1);

which looks basically the same in the other four usual languages, except Raku where there are "+" signs at the start of the bitwise operators.

But because I like the mental gymnastics, I decided to do this one in PostScript as well. You can specify numbers in arbitrary bases, which makes life easier, and this is basically the same algorithm as above.

/seob {

Make a copy of the input.

    dup

Bitwise-and it with the lower mask, and shift up the result.

    16#55 and
    1 bitshift

Swap that on the stack with the other copy of the input.

    exch

Upper mask and shift down the other copy.

    16#AA and
    -1 bitshift

And combine them.

    or
} def

And then I wrote a basic test harness, because why wouldn't I? That runs the tests and spits out a table with input, output, and pass/fail status. It can also be conveniently invoked, at least where GhostScript is available, with ps2txt, which runs the PostScript code, converts the page image to plain text, and dumps it to standard output:

$ ps2txt ch-1.ps 
  1.  101  154 Pass
  2.  18   33  Pass
$

(I've written PostScript code before now to send stuff explicitly to stdout, but that's not what PostScript wants to do in more than a very basic way and it makes it appropriately fiddly.)

TASK #2 › Clock Angle

You are given time $T in the format hh:mm.

Write a script to find the smaller angle formed by the hands of an analog clock at a given time.

Which seems again as though it has a fairly straightforward solution: determine the angle of each hand, then reduce the difference. Raku:

sub ca($n) {
  my $a=0;

Extract hour and minute:

  if ($n ~~ /(<[0..9]>+)\:(<[0..9]>+)/) {

Convert each one to an angle (note that each minute puts half a degree on the hour hand, so we're in floating point territory):

    my ($ha,$ma)=map {$_ % 360}, ($0*30+$1/2,$1*6);

Take the absolute difference:

    $a=abs($ha-$ma);

Reduce until we get an angle lying in (-180..180):

    while ($a > 180) {
      $a-=360;
    }

And take the absolute again.

    $a=abs($a);
  }
  return $a;
}

And it's basically the same algorithm in PostScript, though it looks quite different and I don't have regexps:

/ca {

Split the input string

    (:) search

We now have, for the first test case, (10) (:) (03) true. Throw away the true (I don't care about error handling here).

    pop

Get the hour angle (hour component only) and roll it to the bottom of the working stack. Throw away the (:).

    cvi 30 mul
    3 1 roll pop

We now have 90 (10). Make that an integer, copy it, and get the minute angle.

    cvi dup
    6 mul

We now have 90 10 60. Stow that minute angle at the bottom.

    3 1 roll

We now have 60 90 10. Generate the minute component of the hour angle and add it to the hour component we already have.

    2 div add

We now have 60 95, minute angle and hour angle. Take the difference and reduce.

    sub
    abs
    {
        dup 180 gt
        { 360 sub }
        { exit }
        ifelse
    } loop
    abs
} def

It's very refreshing to write in a completely different paradigm from time to time. I still need to make notes on what's where in the stack as I go along, particularly when roll gets involved. (I'm not sure I have a feel for what's a "natively PostScript" coding style, but using the stack rather than defining local variables, and doing stuff in the order it shows up on the stack rather than shoving it into the order most convenient for me, seem to be a part of it.)

Full code on github.


  1. Posted by RogerBW at 04:04pm on 12 July 2021

    Part 1 seems most easily done by bitshifting, though there were some interesting approaches using arrays of two-character strings. (Perl can split a string into two-character chunks without needing Raku's comb, though it's a bit fiddly.) Some people haven't been reading the blogs to be aware of how oct() can also parse a binary string. (Granted, it's not the sort of thing you'd notice if you were guessing function names…)

    A more straightforward approach to part 2: calculate the minutes since 00:00, multiply by 5.5 degrees per minute, then modulo 360. Here's a PostScript version to do that, just for fun:

    /ca {
        (:) search
        pop
        cvi 60 mul
        exch pop
        exch cvi
        add 5.5 mul
        {
            dup 180 gt
            { 360 sub }
            { exit }
            ifelse
        } loop
        abs
    } def
    

Comments on this post are now closed. If you have particular grounds for adding a late comment, comment on a more recent post quoting the URL of this one.

Search
Archive
Tags 1920s 1930s 1940s 1950s 1960s 1970s 1980s 1990s 2000s 2010s 3d printing action advent of code aeronautics aikakirja anecdote animation anime army astronomy audio audio tech aviation base commerce battletech beer boardgaming book of the week bookmonth chain of command children chris chronicle church of no redeeming virtues cold war comedy computing contemporary cornish smuggler cosmic encounter coup covid-19 crime cthulhu eternal cycling dead of winter doctor who documentary drama driving drone ecchi economics en garde espionage essen 2015 essen 2016 essen 2017 essen 2018 essen 2019 essen 2022 essen 2023 existential risk falklands war fandom fanfic fantasy feminism film firefly first world war flash point flight simulation food garmin drive gazebo genesys geocaching geodata gin gkp gurps gurps 101 gus harpoon historical history horror hugo 2014 hugo 2015 hugo 2016 hugo 2017 hugo 2018 hugo 2019 hugo 2020 hugo 2022 hugo-nebula reread in brief avoid instrumented life javascript julian simpson julie enfield kickstarter kotlin learn to play leaving earth linux liquor lovecraftiana lua mecha men with beards mpd museum music mystery naval noir non-fiction one for the brow opera parody paul temple perl perl weekly challenge photography podcast politics postscript powers prediction privacy project woolsack pyracantha python quantum rail raku ranting raspberry pi reading reading boardgames social real life restaurant reviews romance rpg a day rpgs ruby rust scala science fiction scythe second world war security shipwreck simutrans smartphone south atlantic war squaddies stationery steampunk stuarts suburbia superheroes suspense television the resistance the weekly challenge thirsty meeples thriller tin soldier torg toys trailers travel type 26 type 31 type 45 vietnam war war wargaming weather wives and sweethearts writing about writing x-wing young adult
Special All book reviews, All film reviews
Produced by aikakirja v0.1