ETOOBUSY 🚀 minimal blogging for the impatient
PWC162 - ISBN-13
TL;DR
Here we are with TASK #1 from The Weekly Challenge #162. Enjoy!
The challenge
Write a script to generate the check digit of given ISBN-13 code. Please refer wikipedia for more information.
Example
ISBN-13 check digit for '978-0-306-40615-7' is 7.
The questions
I’m assuming that no validation of the inputs have to be done, like making sure that there are 12+1 digits, separators are in the right place, etc.
The solution
Let’s first reshuffle the formula for calculating the goal value:
\[x_{13} = (10 - ((x_1 + 3 x_2 + x_3 + 3 x_4 + ... + x_{11} + 3 x_{12}) \mod{10})) \mod{10} \\ x_{13} = (- (x_1 + 3 x_2 + x_3 + 3 x_4 + ... + x_{11} + 3 x_{12})) \mod{10} \\ x_{13} = (-x_1 - 3 x_2 - x_3 - 3 x_4 + ... - x_{11} - 3 x_{12}) \mod{10} \\ x_{13} = \sum_{i = 1}^{6} (-x_{2i-1} - 3 x_{2i}) \mod{10}\]So we can take pairs of consecutive digits and address them as in the parenthesis in the summation above.
Let’s start Raku:
#!/usr/bin/env raku
use v6;
sub MAIN (Str:D $input = '978-0-306-40615-7') {
put "ISBN-13 check digit for '$input' is {isbn_13($input)}.";
}
sub isbn_13 ($input) {
$input.comb(/\d/)[0..11] # focus on first 12 digits
.map({-$^a - 3 * $^b}) # apply equivalent weights
.sum % 10; # sum and take remainder
}
We’re concentrating on the first 12 digits, so the comb
gets the
digits and the slice [0..11]
takes the first 12 of them. The following
map
/sum
tandem implements the formula seen above.
Let’s go Perl now, trying to show some Raku-ish dialect!
#!/usr/bin/env perl
use v5.24;
use warnings;
use experimental 'signatures';
no warnings 'experimental::signatures';
use List::Util qw< pairmap sum >;
my $input = shift // '978-0-306-40615-7';
say "ISBN-13 check digit for '$input' is @{[isbn_13($input)]}.";
sub isbn_13 ($input) {
sum(
pairmap { -$a - 3* $b }
($input =~ m{(\d)}gmxs)[0 .. 11]
) % 10;
}
Function pairmap
only appeared in version 1.29 of List::Util, but
we are OK with the constraint on perl
’s version which appeared three
years later (give or take a couple of months). It allows us to replicate
the map taking two values at a time behaviour that comes stock in
Raku, so why not?
The other rakuism is in the embedding of the call to isbn_13
directly into the string:
say "ISBN-13 check digit for '$input' is @{[isbn_13($input)]}.";
# ^^^^^^^^^^^^^^^^^^^^
This is hackish in Perl, although it also funnily gets an aura of secret operator (apparently, it’s the Babycart operator).
Stay safe!