Perl Weekly Challenge 90
Today was a busy day with both Advent of Code and the Perl Weekly Challenge. Luckily, the PWC was short and sweet this week, and a nice breather after the Advent of Code.
Task 1: DNA Sequence
You are given DNA sequence, GTAAACCCCTTTTCATTTAGACAGATCGACTCCTTATCCATTCTCAGAGATGTGTTGCTGGTCGCCG.
Write a script to print nucleotide count in the given DNA sequence. Also print the complementary sequence where Thymine (T) on one strand is always facing an adenine (A) and vice versa; guanine (G) is always facing a cytosine (C) and vice versa.
Solution
See below for explanation and any implementation-specific comments.
subset ValidDna of Str where { $_ ~~ /^^[A|T|G|C]+$$/ }
sub MAIN($dna where $dna ~~ ValidDna, Bool :rc(:$reverse-complement) = False) { # [1]
say "Input stats:\n{$dna.comb.Bag.Hash}\n"; # [2]
say "Complement:";
my $translated = $dna.trans('ATGC' => 'TACG'); # [3]
if $reverse-complement {
say "5'-{$translated.flip}-3'";
} else {
say "3'-$translated-5'"
}
}
This program runs as such:
$ raku ch-1.raku GTAAACCCCTTTTCATTTAGACAGATCGACTCCTTATCCATTCTCAGAGATGTGTTGCTGGTCGCCG
Input stats:
A 14
C 18
G 13
T 22
Complement:
3'-CATTTGGGGAAAAGTAAATCTGTCTAGCTGAGGAATAGGTAAGAGTCTCTACACAACGACCAGCGGC-5'
$ raku ch-1.raku --reverse-complement GTAAACCCCTTTTCATTTAGACAGATCGACTCCTTATCCATTCTCAGAGATGTGTTGCTGGTCGCCG
Input stats:
A 14
C 18
G 13
T 22
Complement:
5'-CGGCGACCAGCAACACATCTCTGAGAATGGATAAGGAGTCGATCTGTCTAAATGAAAAGGGGTTTAC-3'
Explanation
Coming from a Biochemistry background, this one is rather simple. However, the question does not take into consideration that DNA has a direction. Generally, if you are handed a string like this, you expect it to be read from 5’ to 3’ (5 prime to 3 prime). Because of the directionality, the opposite strand would be 3’ to 5’.
So generally, when a question asks for the complementary strand, they want the reverse complement, so it is shown 5’ to 3’. This question did not specify, so I wrote an answer that handles both.
Specific comments
- This syntax allows us to give aliases to our command line arguments. In this case, we accept either
--rc
or--reverse-complement
and store it in a variable called$reverse-complement
. - A
Bag
is a weighted collection. When passed theList
produced by.comb
, it generates a bag with the weight of each letter. We only convert it to aHash
for pretty printing. - Raku’s
trans
method is a single-pass method, meaning you won’t translateA
toT
and then accidentally translate it back toA
with a second pass.
Task 2: Ethiopian Multiplication
You are given two positive numbers $A
and $B
.
Write a script to demonstrate Ethiopian Multiplication using the given numbers.
Solution
See below for explanation and any implementation-specific comments.
subset PositiveInt of Int where { $_ ~~ Int && $_ > 0 }
sub generate-pairs($a, $b) {
sprintf("%02d, %02d", $a, $b).put;
if $a == 1 {
(($a, $b),);
} else {
(($a, $b), |generate-pairs($a div 2, $b * 2));
}
}
sub MAIN(PositiveInt $A, PositiveInt $B) {
say "Input: A: $A, B: $B";
say "Divide A by 2 (ignoring remainders) until it is 1. Multiply B by 2 as we go:";
my @pairs = generate-pairs($A, $B);
say "Then, wherever A is odd, we add the Bs together:";
my @odd-bs = @pairs.grep(-> @pair { !(@pair[0] %% 2) }).map(-> @pair { @pair[1] });
say "{@odd-bs.join(' + ')} = {@odd-bs.sum}";
}
This program runs as such:
$ raku ch-2.raku 14 12
Input: A=14, B=12
Divide A by 2 (ignoring remainders) until it is 1. Multiply B by 2 as we go:
14, 12
07, 24
03, 48
01, 96
Then, wherever A is odd, we add the Bs together:
24 + 48 + 96 = 168
Explanation
This is a very straightforward solution; most of the mess in the code is from the messages printed out. Basically, we generate our pairs (and print them) using recursion, then filter them down to where $A
is odd, and add up the $B
s.
Final Thoughts
This week was super easy, so not much to add! Jump over to my blog on day 7 of the Advent of Code for something more challenging.