Exercise 8.1: Solve problems with map, reduce and filter
Here is a series of simple tasks which you could easily solve with a for-loop in most cases. However the challenge here is to solve the problems without using a for-loop and instead use one of the map, reduce and filter functions.
Exercise 8.1a: Add numbers
Implement sumup(xs) which adds up all the numbers in an iterable collection xs. It should be usable like the builtin sum function.
function sumup(xs)endusing Test "sumup tests" begin sumup([1, 1, 1]) == 3 sumup(1:4) == 10 sumup(4) == 4endExercise 8.1b: Factorial
Implement fac(n) which calculates the factorial of the number n. The factorial of 6 is 6! = 6 * 5 * 4 * 3 * 2 * 1 = 720, while the factorial of 3 is 3! = 3 * 2 * 1 = 6.
function fac(n) endusing Test "fac tests" begin fac(3) == 6 fac(6) == 720 fac(0) == 1endExercise 8.1c: Find length of longest word
Define longest(strings) to find the length of the string in the collection of strings called strings.
function longest(strings)endusing Test "finding longest word tests" begin longest(["one"]) == 3 longest(["one", "two", "three"]) == 5 longest(["twelve", "two", "three"]) == 6 endExercise 8.2: Implement the zip function
The zip function appears in many functional languages and also exists in the Julia standard library. It is used to combine two or more separate lists of objects into a single list of composite objects, such as a tuple.
Julia's zip can work with any number of iterable collections but we want to make a simplified version called combine which takes two collections and combine them to one.
function combine(xs, ys)endusing Test "combine tests" begin combine(1:3, 'a':'c') == collect(zip(1:3, 'a':'c')) combine([4, 8, 10], ["foo", "bar", "qux"]) == collect(zip([4, 8, 10], ["foo", "bar", "qux"])) DimensionMismatch("dimensions must match") combine([4, 8, 10], ["foo", "bar"])endExercise 8.3: Implement a "zip with" function
In some functional languages there is a function called zipwith which is a more generic version of zip. It lets you specify the function which should combine two elements.
function zipwith(f, xs, ys) endusing Test "zipwith tests" begin zipwith(tuple, [4, 8, 10], ["foo", "bar", "qux"]) == [ (4, "foo"), (8, "bar"), (10, "qux")] zipwith(=>, [4, 8, 10], ["foo", "bar", "qux"]) == [ 4 => "foo", 8 => "bar", 10 => "qux"] zipwith(+, 1:4, ones(Int, 4)) == [2, 3, 4, 5]endExercise 8.4: Reverse Polish Notation Calculator
Reverse polish notation is a way of expressing calculations which does not require parentesis to express the order of computations. Calculations are based on having a stack of inputs values and operations.
It is easiest to explain with some examples. 3 + 4 in reverse polish notation (RPN) would be written as 3 4 +. Basically you list the numbers you want to do something with and then you specify the operation you want to do on them. This is beneficial in complex expressions.
(3 + 4) * 210*2 - 2 * (3 + 2)These expressions would in RPN turn into:
3 4 + 2 *10 2 * 2 3 2 + * -Let us look at the first expression. We read from left to right.
Start by putting 3 and 4 on the stack.
When we encounter
+we pop the two top elements from the stack which are 3 and 4 and add them. This gives 7, which is put on the top of the stack.Put 2 on the stack. The stack now looks like
[7, 2]Encounter
*operator. Pop top values from the stack and multiply them.
We end up with 14 as the result.
The following functions will be useful in this exercise:
vcatvertical concatenation of arrays. Remember regular 1D arrays in Julia are column vectors, hence stitching them together is vertical concatenation.reduceUseful whenever you are processing a list of elements and want to turn them into one value....splat operator. Turn an array, tuple etc into multiple function arguments.
input = [10, 4, 3, +, 2, *, -] # solve_rpn(input) should give -4 as answerfunction solve_rpn(xs)endusing Test "solve RPN tests" begin solve_rpn(input) == [-4]endExercise 8.4b: Parse string input (tricky)
Imagine you are reading an expression directly from the command line or from a file. You will not get the input as a neat Julia array of numbers and operations. Instead you will get a string which you need to parse.
Add a method to solve_rpn which processes string input. In this case we need to be introduced to some new Julia functions:
Meta.parsethis is different fromparsewhich is just for parsing types.Meta.parsecan parse any Julia expression, and turn it into Julia code. Or more specifically it will turn it into expressions made up of symbols and literals.evalwill take Julia code and evaluate it. So a symbol such as:+will turn into the function object+. The number literal4will just turn into the number4. Strings and numbers just evaluate to themselves.
function solve_rpn(s::AbstractString)end "solve RPN parsing tests" begin solve_rpn("3 4 +") == [7] solve_rpn("3 4 + 2 *") == [14] solve_rpn("10 4 3 + 2 * -") == [-4]end