From cfab37d1abbb12968c3adeedea14efefad54d2c5 Mon Sep 17 00:00:00 2001 From: Patrick Spek Date: Tue, 11 Sep 2018 11:47:22 +0200 Subject: Add "Hackerrank solutions: Python 3 and Perl 6 (part 1)" --- ...kerrank-solutions-python3-and-perl6-part-1.adoc | 445 +++++++++++++++++++++ 1 file changed, 445 insertions(+) create mode 100644 _posts/2018-09-06-hackerrank-solutions-python3-and-perl6-part-1.adoc (limited to '_posts') diff --git a/_posts/2018-09-06-hackerrank-solutions-python3-and-perl6-part-1.adoc b/_posts/2018-09-06-hackerrank-solutions-python3-and-perl6-part-1.adoc new file mode 100644 index 0000000..c054308 --- /dev/null +++ b/_posts/2018-09-06-hackerrank-solutions-python3-and-perl6-part-1.adoc @@ -0,0 +1,445 @@ +--- +tags: Hackerrank Perl6 Python3 Programming Comparison +description: > + A number of solutions to Hackerrank challenges in both the Python 3 and the + Perl 6 programming languages. Compare the results and see which language + works best for you! +--- += Hackerrank solutions: Python 3 and Perl 6 (part 1) +:toc: preamble + +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. + +[INFO] +==== +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! +==== + +== 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. + +[source,py3] +---- +def solveMeFirst(a,b): + return a+b +---- + +The Perl 6 variant isn't going to very different here. + +[source,perl6] +---- +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. + +[source,py3] +---- +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. + +[source,perl6] +---- +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. + +[source,perl6] +---- +sub simple-array-sum (@ar) { + [+] @ar +} +---- + +=== 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. + +[source,py3] +---- +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. + +[source,perl6] +---- +sub compare-triplets (@a, @b) { + @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. + +=== 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. + +[source,py3] +---- +def aVeryBigSum(ar): + sum = 0 + + for i in ar: + sum += i + + return sum +---- + +And for Perl 6 using the `[+]` reduce meta-operation. + +[source,perl6] +---- +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. + +[source,py3] +---- +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. + +[source,perl6] +---- +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 `_`. + +[source,py3] +---- +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. + +[source,perl6] +---- +sub staircaise ($n) { + for 1..($n+1) -> $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. + +[source,py3] +---- +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. + +[source,perl6] +---- +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. + +[source,py3] +---- +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. + +[source,perl6] +---- +sub birthday-cake-candles (@ar) { + my ($size, $count) = (0, 0); + + for @ar { + if ($_ > $size) { + $size = $_; + $count = 0; + } + + $count++ if $size == $_; + } + + $count; +} +---- + +=== 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. + +[source,py3] +---- +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. + +[source,perl6] +---- +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 link:/[homepage]. + -- cgit v1.1