I’ve been doing the Weekly
Challenges. The
latest
involved digit substrings and securities validation. (Note that this
is open until 24 October 2021.)
Task 1: Middle 3-digits
You are given an integer.
Write a script find out the middle 3-digits of the given integer, if
possible otherwise throw sensible error.
So the steps here are something like:
- take positive value of input
- convert to string
- make sure it's long enough and of an odd length
- find the middle character, and extract it and the characters to each
side
- return them (I choose to do so as a string, since the first one
might legitimately be 0)
Thus:
sub m3d {
my $n=shift;
my $m=abs($n);
$m="$m";
my $l=length($m);
if ($l < 3) {
return 'too short';
}
if ($l % 2 == 0) {
return 'even number of digits';
}
return substr($m,int($l/2)-1,3);
}
(This does mean that the test input 10
will return "too short"
rather than "even number of digits" as given in the examples, but I
find it hard to take that seriously as a problem. I'd rather do a
straight comparison than a modulus as my first step.)
Given that I'm doing this cross-language and want to test for error
messages I just stuff the error into the output string.
The other languages are similar. Some let me chain conversions, such
as Rust's let m=n.abs().to_string();
while some insist on a string
slice being given as start-and-end rather than start-and-length
(Python and Rust as far as I can tell). PostScript doesn't have early
return so I end up using nested conditionals; I also need to take the
log of the number to find out how long a string I need to put it in.
/m3d {
abs
dup log cvi 1 add string /sform exch def
sform cvs pop
sform length
dup 3 lt {
pop
(too short)
} {
dup 2 mod 0 eq {
pop
(even number of digits)
} {
2 idiv 1 sub
sform exch 3 getinterval
} ifelse
} ifelse
} def
Task 2: Validate SEDOL
You are given 7-characters alphanumeric SEDOL.
Write a script to validate the given SEDOL. Print 1 if it is a valid
SEDOL otherwise 0.
The Wikipedia page gives a
starting point, in that a valid one:
- is 7 characters long
- each character lies in [0-9A-Z] and isn't a vowel
- each character has a value (0-9 = 0-9, B=11, C=12, etc., not
skipping vowels in the count)
- the sum of character values mulitiplied by position weights, mod 10,
is zero.
The fiddly bit is validating the letters and getting their values.
Rust is cleanest:
fn sedolv(sedol: &str) -> u8 {
if sedol.len() != 7 {
return 0;
}
let w=vec![1,3,1,7,3,9,1].into_iter();
let mut s=0;
let j=w.zip(sedol.chars().into_iter());
So here I have two joined iterators, the character from the string and
the weight assigned to that position. (I did this in Ruby too. Python
apparently can manage it, but it works differently. Perl/Raku/Python
got a simple index.)
Validating and decoding become relatively easy because I can just
treat each character as a base-36 number.
for x in j {
let n;
if x.1.is_digit(10) ||
(x.1.is_ascii_alphabetic() && x.1.is_ascii_uppercase() &&
x.1 != 'A' && x.1 != 'E' && x.1 != 'I' &&
x.1 != 'O' && x.1 != 'U') {
n=x.1.to_digit(36).unwrap();
} else {
return 0;
}
s += n*x.0;
}
if s % 10 == 0 {
return 1;
}
0
}
In Perl the relevant work is done with regular expressions of course:
if ($c =~ /[0-9]/) {
$n=$c+0;
} elsif ($c =~ /[B-Z]/ && $c !~ /[EIOU]/) {
$n=ord($c)-55;
} else {
return 0;
}
$s+=$n*$w[$i];
Python probably has the base36 decoder somewhere, but:
if sedol[i].isdigit():
n=int(sedol[i])
elif sedol[i].isalpha() and sedol[i].isupper() and not sedol[i] in "AEIOU":
n=ord(sedol[i])-55
else:
return 0
And in PostScript, again lacking early return, I just invalidate and
continue…
/sedolv {
/sedol exch def
/weight [ 1 3 1 7 3 9 1 ] def
/valid 1 def
/sum 0 def
sedol length 7 ne { 0 exit } if
0 1 6 {
dup
sedol exch get
valid 0 eq { exit } if
dup 48 ge exch dup 57 le 3 -1 roll and {
48 sub
} {
dup 66 ge exch dup 90 le 3 -1 roll and {
dup 69 eq { /valid 0 def } if
dup 73 eq { /valid 0 def } if
dup 79 eq { /valid 0 def } if
dup 85 eq { /valid 0 def } if
55 sub
} {
/valid 0 def
} ifelse
} ifelse
exch weight exch get mul sum add /sum exch def
} for
sum 10 mod 0 ne {
/valid 0 def
} if
valid
} def
Full code on
github.
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.