In this sub-chapter you will find exemplary solutions to the exercises from the previous section.
Since I’m eating a surface, and the task description gives me diameters, then I should probably calculate area of a circle. I will use Base.MathConstants.pi in my calculations.
function getCircleArea(radius::Real)::Real
return pi * radius * radius
end
Now, we can finally get the answer.
# radius = diameter / 2
(getCircleArea(30/2) * 2, getCircleArea(45/2))
(1413.7166941154069, 1590.431280879833)
It seems that I will get more food while ordering this one pizza (45 cm in diameter) and not those two pizzas (each 30 cm in diameter).
Note: Instead of
pi * radius * radius
I could have usedradius^2
, where^
is an exponentiation operator in Julia. If I want to raise 2 to the fourth power I can either type2^4
or2*2*2*2
and get 16.
If all the pizzas were cylinders of equal heights (say 2 cm or an inch each) then I would calculate their volumes like so
function getCylinderVolume(radius::Real, height::Real=2)::Real
# hmm, is cylinder just many circles stacked one on another?
return getCircleArea(radius) * height
end
and the results
# radius = diameter / 2
(getCylinderVolume(30/2) * 2, getCylinderVolume(45/2))
(2827.4333882308138, 3180.862561759666)
Still, it appears the conclusion is the same.
My solution to that problem would look something like
function areApproxEqual(f1::Float64, f2::Float64)::Bool
return round(f1, digits=16) == round(f2, digits=16)
end
Let’s put it to the test
areApproxEqual(0.1*3, 0.3)
true
Seems to be working fine. Still, you may prefer to use Julia’s built-in isapprox. In general, it is a good idea to use a built in function from the standard library over your own as it should be more robust.
Anyway, let’s test isapprox
as well.
isapprox(0.1*3, 0.3)
# compare with
# isapprox(0.11*3, 0.3)
# or to test if the values are not equal
# !isapprox(0.11*3, 0.3)
true
It works just fine.
Lesson to be learned here. If you want to do something you can:
Possible solution
function getMax(vect::Vector{Int}, isSortedDesc::Bool)::Int
return isSortedDesc ? vect[1] : sort(vect)[end]
end
(getMax([3, 2, 1], true), getMax([2, 3, 1], false))
(3, 3)
or if you read the documentation for sort
function getMax(vect::Vector{Int}, isSortedDesc::Bool)::Int
return isSortedDesc ? vect[1] : sort(vect, rev=true)[1]
end
(getMax([3, 2, 1], true), getMax([2, 3, 1], false))
(3, 3)
Sorting an array to get the maximum (or minimum) value is not the most effective method (sorting is based on rearranging elements and takes quite some time). Traveling through an array only once should be faster. Therefore probably a better solution (in terms of performance) would be something like
function getMaxUnsorted(unsortedVect::Vector{Int})::Int
maxVal::Int = unsortedVect[1]
for elt in unsortedVect[2:end]
if maxVal < elt
maxVal = elt
end
end
return maxVal
end
function getMax(vect::Vector{Int}, isSortedDesc::Bool)::Int
return isSortedDesc ? vect[1] : getMaxUnsorted(vect)
end
(getMax([3, 2, 1], true), getMax([2, 3, 1], false))
(3, 3)
Read it carefully and try to figure out how it works.
Note: Julia already got similar functionality to
getMin
,getMax
that we developed ourselves. See min, max, minimum, and maximum.
Perhaps the most direct version of the program would be
function printFizzBuzz()
for i in 1:30
# or: if rem(i, 15) == 0
if rem(i, 3) == 0 && rem(i, 5) == 0
println("Fizz Buzz")
elseif rem(i, 3) == 0
println("Fizz")
elseif rem(i, 5) == 0
println("Buzz")
else
println(i)
end
end
return nothing
end
Note: Julia applies operators based on precedence and associativity. If you are unsure about the order of their evaluation (e.g. in
if rem(i, 3) == 0 && rem(i, 5) == 0
) then check the docs or use parenthesis()
to enforce the desired order of evaluation (e.g.if (rem(i, 3) == 0) && (rem(i, 5) == 0)
).
Go ahead, test it out.
If you like challenges try to follow the execution of the following program.
function getFizzBuzz(num::Int)::String
return (
rem(num, 15) == 0 ? "Fizz Buzz" :
rem(num, 3) == 0 ? "Fizz" :
rem(num, 5) == 0 ? "Buzz" :
string(num)
)
end
function printFizzBuzz()
foreach(x -> println(getFizzBuzz(x)), 1:30)
return nothing
end
# you can use it like so: printFizzBuzz()
There are probably other more creative [or more (unnecessarily) convoluted] ways to solve this task. Personally, I would be satisfied if you understand the first version.
For more information about the legend see this Wikipedia’s article.
If you want some more detailed mathematical explanation you can read that Wikipedia’s article.
The Wikipedia’s version of the legend differs slightly from mine, but I like mine better.
Anyway let’s jump right into some looping.
function getNumOfGrainsOnField64()::Int
noOfGrains::Int = 1 # no of grains on field 1
for _ in 2:64
noOfGrains *= 2 # *= is update operator similar to +=
end
return noOfGrains
end
getNumOfGrainsOnField64()
-9223372036854775808
Hmm, that’s odd, a negative number.
Wait a moment. Now I remember, a computer got finite amount of memory. So in order to work efficiently data is stored in small pre-allocated pieces of it. If the number you put into that small ‘memory drawer’ is greater than the amount of space then you get strange results (imagine that a number sticks out of the drawer but Julia looks only at the part inside the drawer, hence the strange result).
If you are interested in technical stuff then you can read more about it in Julia’s docs (sections Integers and Overflow Behavior).
You can check the minimum and maximum value for Int
by typing typemin(Int)
and typemax(Int)
on my laptop those are -9223372036854775808 and 9223372036854775807, respectively.
The broad range of Int
is enough for most calculations, still if you expect a really big number you should use BigInt (BigInt
calculations are slower than the ones for Int
, but now you should be only limited by the amount of memory on your computer).
So let me correct the code.
function getNumOfGrainsOnField64()::BigInt
noOfGrains::BigInt = 1 # no of grains on field 1
for _ in 2:64
noOfGrains *= 2
end
return noOfGrains
end
getNumOfGrainsOnField64()
9223372036854775808
Whoa, that number got like 19 digits. I don’t even know how to name it. It cannot be that big, can it?
OK, quick verification with some mathematical calculation (don’t remember ^
? See Section 3.9.1).
BigInt(2)^63 # we multiply 2 by 2 by 2, etc. for fields 2:64
9223372036854775808
Yep, the numbers appear to be the same.
getNumOfGrainsOnField64() == BigInt(2)^63
true
So I guess the aforementioned Wikipedia’s article is right, it takes much more grain than a country (or the world) could produce in a year.
A possible solution with generics looks something like that
function getInit(vect::Vector{T})::Vector{T} where T
return vect[1:(end-1)]
end
getInit (generic function with 1 method)
The parenthesis around end-1
are not necessary. I added them for better clarity of how the last by one index is calculated.
Tests:
getInit([1, 2, 3, 4])
[1, 2, 3]
getInit(["ab", "cd", "ef", "gh"])
["ab", "cd", "ef"]
getInit([3.3])
Float64[]
getInit([])
BTW. Try to remove type declarations and see if the function still works (if you do this right then it should).
OK, that’s it for now. Let’s move to another chapter.