summaryrefslogtreecommitdiff
path: root/content/posts/2018/2018-09-13-hackerrank-solutions-python3-and-perl6-part-1.md
diff options
context:
space:
mode:
Diffstat (limited to 'content/posts/2018/2018-09-13-hackerrank-solutions-python3-and-perl6-part-1.md')
-rw-r--r--content/posts/2018/2018-09-13-hackerrank-solutions-python3-and-perl6-part-1.md445
1 files changed, 445 insertions, 0 deletions
diff --git a/content/posts/2018/2018-09-13-hackerrank-solutions-python3-and-perl6-part-1.md b/content/posts/2018/2018-09-13-hackerrank-solutions-python3-and-perl6-part-1.md
new file mode 100644
index 0000000..c7418e7
--- /dev/null
+++ b/content/posts/2018/2018-09-13-hackerrank-solutions-python3-and-perl6-part-1.md
@@ -0,0 +1,445 @@
+---
+title: "Hackerrank Solutions: Python 3 and Perl 6 (part 1)"
+date: 2018-09-13
+tags:
+- Hackerrank
+- Perl6
+- Python
+- Python3
+- Programming
+- Raku
+---
+
+# Hackerrank solutions: Python 3 and Perl 6 (part 1)
+
+I recently started at a new company, for which I will have to write Python 3
+code. To make sure I still know how to do basic stuff in Python, I started to
+work on some [https://www.hackerrank.com/](Hackerrank challenges). In this post,
+I will show solutions to some challenges to show the differences. I hope that I
+can show that Perl doesn't have to be the "write only" language that many
+people make it out to be.
+
+{< admonition title="note" >}
+I am _much_ more proficient in the Perl 6 programming language than in Python
+(2 or 3), so I might not always use the most optimal solutions in the Python
+variants. Suggestions are welcome via email, though I most likely won't update
+this post with better solutions. I ofcourse also welcome feedback on the Perl 6
+solutions!
+{< / admonition >}
+
+## Challenges
+
+The challenges covered in this post are the
+[https://www.hackerrank.com/domains/algorithms?filters%5Bsubdomains%5D%5B%5D=warmup](warmup
+challenges) you are recommended to solve when you make a new account. The code
+around the function I'm expected to solve won't be included, as this should be
+irrelevant (for now). Additionally, I may rename the sub to conform to
+[https://en.wikipedia.org/wiki/Letter_case#Special_case_styles](kebab-case), as
+this is more readable (in my opinion), and allowed in Perl 6.
+
+### Solve Me First
+
+This challenge is just a very simple example to introduce how the site works.
+It required me to make a simple `a + b` function.
+
+```python3
+def solveMeFirst(a,b):
+ return a+b
+```
+
+The Perl 6 variant isn't going to very different here.
+
+```raku
+sub solve-me-first ($a, $b) {
+ $a + $b
+}
+```
+
+For those not familiar with Perl 6, the `$` in front of the variable names is
+called a [https://docs.perl6.org/language/glossary#index-entry-Sigil](Sigil),
+and it signals that the variable contains only a single value.
+
+You may have noticed that there's also no `return` in the Perl 6 variant of
+this example. In Perl 6, the last statement in a block is also the implicit
+return value (just like in Perl 5 or Ruby).
+
+### Simple Array Sum
+
+For this challenge I had to write a function that would return the sum of a
+list of values. Naturally, I wanted to use a `reduce` function, but Python 3
+does not support these. So I wrote it with a `for` loop instead.
+
+```python3
+def simpleArraySum(ar):
+ sum = 0
+
+ for i in ar:
+ sum += i
+
+ return sum
+```
+
+Perl 6 does have a `reduce` function, so I would use that to solve the problem
+here.
+
+```raku
+sub simple-array-sum (@ar) {
+ @ar.reduce(sub ($a, $b) { $a + $b })
+}
+```
+
+Here you can see a different sigil for `@ar`. The `@` sigil denotes a list of
+scalars in Perl 6. In most other languages this would simply be an array.
+
+This code can be written even shorter, however. Perl 6 has
+[https://docs.perl6.org/language/operators#index-entry-%5B%2B%5D_%28reduction_metaoperators%29](reduction
+meta-operators). This allows you to put an operator between brackets, like
+`[+]`, to apply a certain operator as a reduce function.
+
+```raku
+sub simple-array-sum (@ar) {
+ [+] @ar
+}
+```
+
+{< admonition title="note" >}
+After publishing this post I have learned that both Python 3 and Perl 6 have a
+`.sum` function that can also be called on the array, simplifying the code in
+both languages.
+{< / admonition >}
+
+### Compare the Triplets
+
+This challenge provides you with 2 lists of 3 elements each. The lists should
+be compared to one another, and a "score" should be kept. For each index, if
+the first list contains a larger number, the first list's score must be
+incremented. Similarly, if the second list contains a larger number on that
+index, the second list's score must be incremented. If the values are equal, do
+nothing.
+
+```python3
+def compareTriplets(a, b):
+ scores = [0, 0]
+
+ for i in range(3):
+ if a[i] > b[i]:
+ scores[0] += 1
+
+ if a[i] < b[i]:
+ scores[1] += 1
+
+ return scores
+```
+
+I learned that Python 3 has no `++` operator to increment a value by 1, so I
+had to use `+= 1` instead.
+
+```raku
+sub compare-triplets (@a, @b) {
+ my @scores = [0, 0];
+
+ for ^3 {
+ @scores[0]++ if @a[$_] > @b[$_];
+ @scores[1]++ if @a[$_] < @b[$_];
+ }
+}
+```
+
+In Perl 6, the `^3` notation simply means a range from 0 to 3, non-inclusive,
+so `0`, `1`, `2`, meaning it will loop 3 times. The `$_` is called the
+_topic_, and in a `for` loop it is the current element of the iteration.
+
+Both of these loops could use a `continue` (or `next` in Perl 6) to skip the
+second `if` in case the first `if` was true, but for readability I chose not
+to.
+
+{< admonition title="note" >}
+After publishing this post I learned that Python 3 also supports the inline if
+syntax, just like Perl 6, so I could've used this in Python 3 as well.
+{< / admonition >}
+
+### A Very Big Sum
+
+In this challenge, you need to write the function body for `aVeryBigSum`, which
+gets an array of integers, and has to return the sum of this array. Both Python
+3 and Perl 6 handle the large integers transparently for you, so I was able to
+use the same code as I used for the simple array sum challenge.
+
+```python3
+def aVeryBigSum(ar):
+ sum = 0
+
+ for i in ar:
+ sum += i
+
+ return sum
+```
+
+And for Perl 6 using the `[+]` reduce meta-operation.
+
+```raku
+sub a-very-big-sum (@ar) {
+ [+] @ar
+}
+```
+
+### Plus Minus
+
+The next challenge gives a list of numbers, and wants you to return the
+fractions of its elements which are positive, negative or zero. The fractions
+should be rounded down to 6 decimals. I made a counter just like in the
+*Compare the Triplets* challenge, and calculated the fractions and rounded them
+at the end.
+
+```python3
+def plusMinus(arr):
+ counters = [0, 0, 0]
+
+ for i in arr:
+ if (i > 0):
+ counters[0] += 1
+ continue
+
+ if (i < 0):
+ counters[1] += 1
+ continue
+
+ counters[2] += 1
+
+ for i in counters:
+ print("%.6f" % (i / len(arr)))
+```
+
+For the Perl 6 solution, I went for a `given/when`, `map` and the `fmt`
+function to format the fractions.
+
+```raku
+sub plus-minus (@arr) {
+ my @counters = [0, 0, 0];
+
+ for @arr -> $i {
+ given $i {
+ when * > 0 { @counters[0]++ }
+ when * < 0 { @counters[1]++ }
+ default { @counters[2]++ }
+ }
+ }
+
+ @counters.map({ $_.fmt("%.6f").say });
+}
+```
+
+You may notice a number of statements do not have a terminating `;` at the end.
+In Perl 6, this is not needed if it's the last statement in a block (any code
+surrounded by a `{` and `}`.
+
+The `given/when` construct is similar to a `switch/case` found in other
+languages (but not Python, sadly), but uses the
+[https://docs.perl6.org/language/operators#index-entry-smartmatch_operator](smartmatch
+operator) implicitly to check if the statements given to `when` are `True`. The
+`*` is the [https://docs.perl6.org/type/Whatever](Whatever operator), which in
+this case will get the value of `$i`.
+
+Lastly, he `$_` in the `map` function is similar to inside a `for` loop,
+it's the current element. Since the code given to `map` is inside a block,
+there's no need for a `;` after `say` either.
+
+### Staircase
+
+This challenge gives you an integer 𝓃, and you're tasked with "drawing" a
+staircase that is 𝓃 high, and 𝓃 wide at the base. The staircase must be made
+using `#` characters, and for the spacing you must use regular spaces.
+
+It seems that in Python, you _must_ specify the `i in` part oft the `for i in
+range`. Since I don't really care for the value, I assigned it to `_`.
+
+```python3
+def staircase(n):
+ for i in range(1, n + 1):
+ for _ in range(n - i):
+ print(" ", end="")
+
+ for _ in range(i):
+ print("#", end="")
+
+ print("")
+```
+
+In Perl 6, there's also a `print` function, which is like `say`, but does not
+append a `\n` at the end of the string. The `for` loop in Perl 6 allows for
+just a range to operate as expected. The `..` operator creates a range from the
+left-hand side up to the right hand side, inclusive.
+
+```raku
+sub staircase ($n) {
+ for 1..$n -> $i {
+ print(" ") for 0..($n - $i);
+ print("#") for ^$i;
+ print("\n");
+ }
+}
+```
+
+### Mini-Maxi Sum
+
+Here you will be given 5 integers, and have to calculate the minimum and
+maximum values that can be calculated using only 4 of them.
+
+I sort the array, and iterate over the first 4 values to calculate the sum and
+print it. I then do the same but sort it in reverse for the sum of the 4
+highest values.
+
+```python3
+def miniMaxSum(arr):
+ arr.sort()
+ sum = 0
+
+ for i in range(4):
+ sum += arr[i]
+
+ print(str(sum) + " ", end="")
+
+ arr.sort(reverse=True)
+ sum = 0
+
+ for i in range(4):
+ sum += arr[i]
+
+ print(str(sum))
+```
+
+Perl 6 has immutable lists, so calling `sort` on them will return a new list
+which has been sorted. I can call `reverse` on that list to get the highest
+number at the top instead. `head` allows me to get the first 4 elements in a
+functional way. You've already seen the meta-reduce operator `[+]`, which will
+get me the sum of the 4 elements I got from `head`. I wrap the calculation in
+parenthesis so I can call `print` on the result immediately.
+
+```raku
+sub mini-maxi-sum (@arr) {
+ ([+] @arr.sort.head(4)).print;
+ print(" ");
+ ([+] @arr.sort.reverse.head(4)).print;
+}
+```
+
+### Birthday Cake Candles
+
+In this challenge, you're given a list of numbers. You must find the highest
+number in the list, and return how often that number occurs in the list.
+
+It's fairly straightforward, I keep track of the current largest value as
+`size`, and a `count` that I reset whenever I find a larger value than I
+currently have.
+
+```python3
+def birthdayCakeCandles(ar):
+ size = 0
+ count = 0
+
+ for i in ar:
+ if i > size:
+ size = i
+ count = 0
+
+ if i == size:
+ count += 1
+
+ return count
+```
+
+The Perl 6 variant does not differ in how it solves the problem, apart from
+having a very different syntax of course.
+
+```raku
+sub birthday-cake-candles (@ar) {
+ my ($size, $count) = (0, 0);
+
+ for @ar {
+ if ($_ > $size) {
+ $size = $_;
+ $count = 0;
+ }
+
+ $count++ if $size == $_;
+ }
+
+ $count;
+}
+```
+
+{< admonition title="note" >}
+On IRC, someone showed me a clean solution in Python 3: `return
+ar.count(max(ar))`. This feels like a much cleaner solution than what I had
+created.
+{< / admonition >}
+
+### Time Conversion
+
+This is the final challenge of this section on Hackerrank, and also this post.
+You're given a timestamp in 12-hour AM/PM format, and have to convert it to a
+24-hour format.
+
+I split the AM/PM identifier from the actual time by treating the string as a
+list of characters and taking two slices, one of the last two characters, and
+one of everything _but_ the last two characters. Then I split the time into
+parts, and convert the first part (hours) to integers for calculations. Next I
+set the hours to 0 if it's set to 12, and add 12 hours if the timestamp was
+post meridiem. Finally, I convert the hours back to a string with leading
+zeroes, and join all the parts together to form a timestamp again.
+
+```python3
+def timeConversion(s):
+ meridiem = s[-2:]
+ hours = int(s[:2])
+ rest = s[2:-2]
+
+ if (hours > 11):
+ hours = 0
+
+ if (meridiem.lower() == "pm"):
+ hours += 12
+
+ return ("%02d:%s" % (hours, rest))
+```
+
+The Perl 6 solution again doesn't differ much from the Python solution in terms
+of the logic it's using to get the result. The biggest difference is that in
+Perl 6, strings can't be accessed as lists, so I use the `substr` method to
+extract the parts that I want. The first one starts at `*-2`, which means 2
+places before the end. The others get a
+[https://docs.perl6.org/type/Range](`Range`) as argument, and will get the
+characters that exist in that range.
+
+```raku
+sub time-conversion ($s) {
+ my $meridiem = $s.substr(*-2);
+ my $hours = $s.substr(0..2).Int;
+ my $rest = $s.substr(2..*-2);
+
+ $hours = 0 if $hours > 11;
+ $hours += 12 if $meridiem.lc eq "pm";
+
+ sprintf("%02d:%s", $hours, $rest);
+}
+```
+
+The `.Int` method converts the `Str` object into an `Int` object, so we can
+perform calculations on it. The `eq` operator checks specifically for
+[https://docs.perl6.org/routine/eq](_string equality_). Since Perl 6 is a
+[https://en.wikipedia.org/wiki/Gradual_typing](gradually typed programming
+language), there's a dedicated operator to ensure that you're checking string
+equality correctly.
+
+## Wrap-up
+
+These challenges were just the warm-up challenges I was given after creating a
+new account and choosing Python as a language to use. I intend to write up more
+posts like this, for the near future I'll stick to Python 3 challenges since I
+want to get better at that specific language for work.
+
+This is also the first post in which I have tried this format to show off two
+languages side-by-side, and to highlight differences in how you can accomplish
+certain (relatively simple) tasks with them. If you have suggestions to improve
+this format, do not hesitate to contact me. I am always open for feedback,
+preferably via email. You can find my contact details on the [/](homepage).