It’s been 4 weeks; well past time to blog again; this time for Perl Weekly Challenge 014

What the Van Eck?

The first challenge for me this week was to go to the Wikipedia page, read what it was saying, and try to figure out exactly what the Van Eck sequence is.

This was at 10pm and I’d spent all day learning to adapt to Fedora after installing that on my new laptop (and discovering while trying to attach a NASalike via SSH to my existing workstation that I’d set up SSH to immediately launch a docker container that sandboxed my user into my home directory, thus causing nothing to work as expected). After some heavy screen glaring I managed to arrive at a re-interpretation as three rules:

  • a0 = 0
  • an + 1 = an - am when m is the largest index < n of a having am = an
  • an + 1 = 0 when the conditions for m cannot be met.

This was sufficient for me to then confuse myself into tyring to store the next m and n pair for a given value of a in a hash and fill everything up with 0 in a gather / take loop.

I did a lot of filling everything up with 0.

I replaced this first implementation (sorry, I didn’t save this source so you could all laugh at it) with an eager list generation that would count back from the current n to 0 instead:

my $m = Van_Eck[max(0, $n-1)...0]:k.first({Van_Eck[$_] == Van_Eck[$n]});
take defined($m) ?? $n - $m !! 0;

This works, but is quite slow, probably mainly because of the eager list generation. A keen observer might also notice that Van_Eck[max(0, $n-1)...0]:k is a bit silly, given that getting the keys of a list that you’ve generated by specifying the keys of another list is just going to give you the same list you’ve specified back, so I may as well have written (max(0, $n-1)...0), but I went to bed and discovered this the next morning instead, laughed out loud, and fixed it; my 7 year old daughter also thought it funny.

This didn’t solve the speed issue though (even if said problem was only noticeable when you gave the program an obscene upper bound), so I revisited using a hash to find the highest index the same as an and the sleep-having version of myself quickly realised that I only needed to store one index per hash element, and whether such an m existed could be determined using the :exists adverb.

After a little bit of failing to realise I never needed to explicitly update %m{0} (and thus filling everything up with 0 again) I finally managed to come up with something that wouldn’t stall massively when asked to find 2000 elements in the sequence:

my \Van_Eck := gather {
  take $start;
  my %m = $start => 0;
  
  for ^ -> $n {
    take %m{Van_Eck[$n]}:exists ?? $n - %m{Van_Eck[$n]} !! 0;
    %m{Van_Eck[$n]} = $n;
  }
}

Worth noting is that $n is always one behind the index of the current value being taken.

Github link to solution

Spelling with the United States

Perl 6 makes the meat of this almost trivial. Create a set of the states’ abbreviations (I zipped these into a hash of the states’ names for later reference), sort all the even length words you have (e.g. /usr/share/dict/words) into longest to shortest order, then find the first list element where a comb of 2-letter sequences of the word is a subset of the abbreviations.

Using the hash of abbreviation => name I then print out which states spelt the word and the word itself.

Apparently, the longest spellable word in the British English dictionary on my system is cacogalactia, which apparently means that California, Colorado, Georgia, Louisiana, Connecticut & Iowa are together states of bad milk

Github link to solution

What time is it in … ?

This week’s optional API challenge was to Find the given city current time using the Geo DB Cities API.

The Geo DB Cities API has an endpoint that doesn’t require an API key, which is a refreshing change. The documentation on what that end point is seemed to be non-existent, though; after some hunting around in the network panel of Firefox on their demo pages I found what appeared to be it.

I elected again (Ah, I did the optional challenge in week 12, but didn’t blog that one) to use Cro::Http::Client to do the heavy lifting of HTTP connections, and don’t seem to be able to find a URL encoding function for it, so I reused the routine I put together for this last time, but with support for turning spaces into +:

sub url-part-encode (Str(Cool) $in --> Str) {
  $in.trans: (|<! # $ % & ' ( ) * + , / : ; = ? @ [ ]>, ' ') => (|<%21 %23 %24 %25 %26 %27 %28 %29 %2A %2B %2C %2F %3A %3B %3D %3F %40 %5B %5D>, '+')
}

Pretty trivial, but I in no way endorse this as a complete solution for input sanitation. Someone please tell me if I’ve missed something that does this in a more bulletproof manner.

Cro::Http::Client is pretty straightforward to use. You can create a defined instance of it or not, then use the get method (or post, or request as appropriate) to access the data you require. This creates a promise that includes various methods for getting the response, including body which will automatically convert JSON into Perl 6 structures for you. This itself returns a Promise, which seems a bit awkward, although I’m sure there’s a reason for it.

Which brings me onto a new gripe, which is that chaining Promises together feels awkward; I feel like using a Promise in sink context at the end of a then block should chain automatically like it does in ECMAScript6, but it seems they don’t, so I end up with awkward chains like:

$geodb-client.get(...).then({ await .result.body }).then({ if my $cityid = .result<data>[0]<id> {...} });

or worse:

.then({ .result.body }).then({ if my $cityid = .result.result<data>[0]<id> {...} })

Maybe I’ve missed something? Maybe I’ve spent too long in the browser… I guess given that in Diwali await no longer blocks a thread, it may as well be written as:

.then({ if my $cityid = await(.result.body).<data>[0]<id> {...} })

This is particularly relevant in this case, because when you have a city name, you have to make an API call to turn that into an ID, and then you can call the dateTime endpoint on that city ID, so that’s 4 Promises.

I’m also struggling with error handling inside then blocks. I want to fail, but end up with internal MoarVM errors (tried to unwind entire stack…). I guess I have quite a bit to learn there, but didn’t feel like following it to the bitter end this time around.

Github link to solution.