Solutions for chapters 1-10

This commit is contained in:
Wojciech Nagrodzki 2019-01-13 11:13:35 +01:00
parent 7259c70474
commit 892d716709
Signed by: wnagrodzki
GPG key ID: E9D0EB0302264569
19 changed files with 524 additions and 0 deletions

23
Functions-1.exs Normal file
View file

@ -0,0 +1,23 @@
# Go into IEx. Create and run the functions that do the following:
#
# list_concat.([:a, :b], [:c, :d]) #=> [:a, :b, :c, :d]
# sum.(1, 2, 3) #=> 6
# pair_tuple_to_list.( { 1234, 5678 } ) #=> [ 1234, 5678 ]
list_concat = fn [a, b], [c, d] ->
[a, b, c, d]
end
IO.inspect list_concat.([1, 2], [3, 4])
sum = fn [a, b, c] ->
a + b + c
end
IO.puts sum.([1, 2, 3])
pair_tuple_to_list = fn { a, b } ->
[a, b]
end
IO.inspect pair_tuple_to_list.({1, 2})

15
Functions-2.exs Normal file
View file

@ -0,0 +1,15 @@
# Write a function that takes three arguments. If the first two are zero, return “FizzBuzz.”
# If the first is zero, return “Fizz.” If the second is zero, return “Buzz.”
# Otherwise return the third argument. Do not use any language features that we havent yet covered in this book.
fun = fn
{0, 0, _} -> "FizzBuzz"
{0, _, _} -> "Fizz"
{_, 0, _} -> "Buzz"
{_, _, c} -> c
end
IO.puts fun.({0, 0, 1})
IO.puts fun.({0, 1, 1})
IO.puts fun.({1, 0, 1})
IO.puts fun.({1, 1, 1})

22
Functions-3.exs Normal file
View file

@ -0,0 +1,22 @@
# The operator rem(a, b) returns the remainder after dividing a by b.
# Write a function that takes a single integer (n) and calls the function in the previous exercise, passing it rem(n,3), rem(n,5), and n.
# Call it seven times with the arguments 10, 11, 12, and so on. You should get “Buzz, 11, Fizz, 13, 14, FizzBuzz, 16.
fun = fn
{0, 0, _} -> "FizzBuzz"
{0, _, _} -> "Fizz"
{_, 0, _} -> "Buzz"
{_, _, c} -> c
end
fizzBuzz = fn n ->
fun.({rem(n,3), rem(n, 5), n})
end
IO.puts fizzBuzz.(10)
IO.puts fizzBuzz.(11)
IO.puts fizzBuzz.(12)
IO.puts fizzBuzz.(13)
IO.puts fizzBuzz.(14)
IO.puts fizzBuzz.(15)
IO.puts fizzBuzz.(16)

19
Functions-4.exs Normal file
View file

@ -0,0 +1,19 @@
# Write a function prefix that takes a string. It should return a new function that takes a second string.
# When that second function is called, it will return a string containing the first string, a space, and the second string.
#
#   iex> mrs = prefix.("Mrs")
#   #Function<erl_eval.6.82930912>
#   iex> mrs.("Smith")
#   "Mrs Smith"
#   iex> prefix.("Elixir").("Rocks")
#   "Elixir Rocks"”
prefix = fn p ->
fn s ->
"#{p} #{s}"
end
end
mrs = prefix.("Mrs")
IO.puts mrs.("Smith")
IO.puts prefix.("Elixir").("Rocks")

7
Functions-5.exs Normal file
View file

@ -0,0 +1,7 @@
# Use the & notation to rewrite the following:
#
# Enum.map [1,2,3,4], fn x -> x + 2 end
# Enum.each [1,2,3,4], fn x -> IO.inspect x end
IO.inspect Enum.map [1,2,3,4], &(&1 + 2)
Enum.each [1,2,3,4], &(IO.inspect/1)

22
ListsAndRecursion-1.exs Normal file
View file

@ -0,0 +1,22 @@
# Write a mapsum function that takes a list and a function. It applies the function to each element of the list and then sums the result, so
# iex> MyList.mapsum [1, 2, 3], &(&1 * &1)
# 14
defmodule MyList do
def mapsum(list, func) do
mapsum(list, 0, func)
end
defp mapsum([head | tail], acc, func) do
mapsum(tail, acc + func.(head), func)
end
defp mapsum([], acc, _func) do
acc
end
end
IO.puts MyList.mapsum([1, 2, 3], fn (n) -> n end)
IO.puts MyList.mapsum('A', fn (n) -> n end)

25
ListsAndRecursion-2.exs Normal file
View file

@ -0,0 +1,25 @@
# Write a max(list) that returns the element with the maximum value in the list. (This is slightly trickier than it sounds.)
defmodule MyList do
def maxlist([]) do
nil
end
def maxlist([hd | []]) do
hd
end
def maxlist([hd | tl]) do
if hd > maxlist(tl) do
hd
else
maxlist(tl)
end
end
end
IO.puts MyList.maxlist([])
IO.puts MyList.maxlist([1])
IO.puts MyList.maxlist([1, 2, 5, 3])

25
ListsAndRecursion-3.exs Normal file
View file

@ -0,0 +1,25 @@
# An Elixir single-quoted string is actually a list of individual character codes.
# Write a caesar(list, n) function that adds n to each list element, wrapping if the addition results in a character greater than z.
# iex> MyList.cesar('ryvkve', 13)
defmodule MyList do
def cesar([], _n) do
[]
end
def cesar([hd | []], n) do
if hd + n < ?z do
[hd + n]
else
cesar([hd], n - ?z + ?a - 1)
end
end
def cesar([hd | tl], n) do
cesar([hd], n) ++ cesar(tl, n)
end
end
IO.puts MyList.cesar('ryvkve', 13)

16
ListsAndRecursion-4.exs Normal file
View file

@ -0,0 +1,16 @@
# Write a function MyList.span(from, to) that returns a list of the numbers from from up to to.
defmodule MyList do
def span(n, n) do
[n]
end
def span(from, to) do
[from] ++ span(from + 1, to)
end
end
IO.inspect MyList.span(1, 1)
IO.inspect MyList.span(1, 10)

86
ListsAndRecursion-5.exs Normal file
View file

@ -0,0 +1,86 @@
# Implement the following Enum functions using no library functions or list comprehensions: all?, each, filter, split, and take.
# You may need to use an if statement to implement filter. The syntax for this is
defmodule MyEnum do
def all?([], _) do
true
end
def all?( [head | tail], fun) do
# The dot is only used when calling anonymous functions that
# have been bound to a variable (and not functions defined inside a module).
# The dot also reminds us that it is an anonymous function.
fun.(head) && all?(tail, fun)
end
def each([], _) do
# noop
end
def each( [head | tail], fun) do
fun.(head)
each(tail, fun)
end
def filter([], _) do
[]
end
def filter( [head | tail], fun) do
if fun.(head) do
[head] ++ filter(tail, fun)
else
filter(tail, fun)
end
end
def split(list, count) do
split([], list, count)
end
defp split(listA, listB, 0) do
{listA, listB}
end
defp split(listA, [head | tail], count) do
split(listA ++ [head], tail, count - 1)
end
def take(_list, 0) do
[]
end
def take([], _count) do
[]
end
def take( [head | tail], count ) do
[head] ++ take(tail, count - 1)
end
def flatten([]) do
[]
end
def flatten([head | tail]) do
if is_list(head) do
flatten(head) ++ flatten(tail)
else
[head] ++ flatten(tail)
end
end
end
IO.puts MyEnum.all?( [], &(&1 < 4) )
IO.puts MyEnum.all?( [1], &(&1 < 4) )
IO.puts MyEnum.all?( [1, 4], &(&1 < 4) )
MyEnum.each [1, 2, 3], &( IO.puts &1 )
IO.inspect MyEnum.filter [1, 2, 3, 4, 5], &( &1 < 3 )
IO.inspect MyEnum.split [1, 2, 3, 4, 5], 2
IO.inspect MyEnum.take [1, 2, 3, 4, 5], 6

20
ListsAndRecursion-6.exs Normal file
View file

@ -0,0 +1,20 @@
# (Hard) Write a flatten(list) function that takes a list that may contain any number of sublists, which themselves may contain sublists, to any depth.
# It returns the elements of these lists as a flat list.
defmodule MyEnum do
def flatten([]) do
[]
end
def flatten([head | tail]) do
if is_list(head) do
flatten(head) ++ flatten(tail)
else
[head] ++ flatten(tail)
end
end
end
IO.inspect MyEnum.flatten([ 1, [ 2, 3, [4] ], 5, [[[6]]]])

34
ListsAndRecursion-7.exs Normal file
View file

@ -0,0 +1,34 @@
# In the last exercise of Chapter 7, Lists and Recursion, you wrote a span function.
# Use it and list comprehensions to return a list of the prime numbers from 2 to n.
defmodule MyList do
def prime(n) do
numbers = Enum.to_list span(2, n)
for x <- numbers, isPrime(x), do: x
end
defp span(n, n) do
[n]
end
defp span(from, to) do
[from] ++ span(from + 1, to)
end
defp isPrime(1) do
true
end
defp isPrime(2) do
true
end
defp isPrime(n) do
denominators = Enum.to_list span(2, n - 1)
Enum.all? denominators, fn x -> rem(n, x) != 0 end
end
end
IO.inspect MyList.prime(100)

51
ListsAndRecursion-8.exs Normal file
View file

@ -0,0 +1,51 @@
# The Pragmatic Bookshelf has offices in Texas (TX) and North Carolina (NC), so we have to charge sales tax on orders shipped to these states.
# The rates can be expressed as a keyword list (I wish it were that simple.…):
# tax_rates = [ NC: 0.075, TX: 0.08 ]
# Heres a list of orders:
# orders = [
# [ id: 123, ship_to: :NC, net_amount: 100.00 ],
# [ id: 124, ship_to: :OK, net_amount: 35.50 ],
# [ id: 125, ship_to: :TX, net_amount: 24.00 ],
# [ id: 126, ship_to: :TX, net_amount: 44.80 ],
# [ id: 127, ship_to: :NC, net_amount: 25.00 ],
# [ id: 128, ship_to: :MA, net_amount: 10.00 ],
# [ id: 129, ship_to: :CA, net_amount: 102.00 ],
# [ id: 130, ship_to: :NC, net_amount: 50.00 ] ]
# Write a function that takes both lists and returns a copy of the orders, but with an extra field, total_amount, which is the net plus sales tax.
# If a shipment is not to NC or TX, theres no tax applied.
tax_rates = [ NC: 0.075, TX: 0.08 ]
orders= [
[ id: 123, ship_to: :NC, net_amount: 100.00 ],
[ id: 124, ship_to: :OK, net_amount: 35.50 ],
[ id: 125, ship_to: :TX, net_amount: 24.00 ],
[ id: 126, ship_to: :TX, net_amount: 44.80 ],
[ id: 127, ship_to: :NC, net_amount: 25.00 ],
[ id: 128, ship_to: :MA, net_amount: 10.00 ],
[ id: 129, ship_to: :CA, net_amount: 102.00 ],
[ id: 130, ship_to: :NC, net_amount: 50.00 ]
]
defmodule Calculator do
def process(orders, tax_rates) do
for order <- orders, do: order ++ [ total_amount: total_amount(order, tax_rates) ]
end
defp total_amount(order, tax_rates) do
tax_rate = tax_rates[order[:ship_to]]
if tax_rate == nil do
order[:net_amount]
else
order[:net_amount] + order[:net_amount] * tax_rate
end
end
end
IO.inspect Calculator.process(orders, tax_rates)

View file

@ -0,0 +1,27 @@
# Exercise: ModulesAndFunctions-1
# Extend the Times module with a triple function that multiplies its parameter by three.
# Exercise: ModulesAndFunctions-2
# Run the result in IEx. Use both techniques to compile the file.
# Exercise: ModulesAndFunctions-3
# Add a quadruple function. (Maybe it could call the double function.…)
defmodule Times do
def triple(n) do
n*3
end
def quadruple(n) do
double(n) * double(n)
end
defp double(n) do
n*n
end
end
IO.puts Times.triple(1)
IO.puts Times.quadruple(2)

17
ModulesAndFunctions-4.exs Normal file
View file

@ -0,0 +1,17 @@
# Implement and run a function sum(n) that uses recursion to calculate the sum of the integers from 1 to n.
# Youll need to write this function inside a module in a separate file.
# Then load up IEx, compile that file, and try your function.
defmodule MyModule do
def sum(1) do
1
end
def sum(n) do
n + sum(n - 1)
end
end
IO.puts MyModule.sum(10)

16
ModulesAndFunctions-5.exs Normal file
View file

@ -0,0 +1,16 @@
# Write a function gcd(x,y) that finds the greatest common divisor between two nonnegative integers.
# Algebraically, gcd(x,y) is x if y is zero; its gcd(y, rem(x,y)) otherwise.
defmodule MyModule do
def gcd(x,0) do
x
end
def gcd(x,y) do
gcd(y, rem(x, y))
end
end
IO.puts MyModule.gcd(17*5, 13*5)

47
ModulesAndFunctions-6.exs Normal file
View file

@ -0,0 +1,47 @@
# Im thinking of a number between 1 and 1000.…
# The most efficient way to find the number is to guess halfway between the low and high numbers of the range.
# If our guess is too big, then the answer lies between the bottom of the range and one less than our guess.
# If our guess is too small, then the answer lies between one more than our guess and the end of the range.
# Your API will be guess(actual, range), where range is an Elixir range. Your output should look similar to this:
#
#   iex> Chop.guess(273, 1..1000)
#   Is it 500
#   Is it 250
#   Is it 375
#   Is it 312
#   Is it 281
#   Is it 265
#   Is it 273
#   273
# Hints:
# - You may need to implement helper functions with an additional parameter (the currently guessed number).
# - The div(a,b) function performs integer division.
# - Guard clauses are your friends.
# - Patterns can match the low and high parts of a range (a..b=4..8).
defmodule Chop do
def guess(actual, range) do
first..last = range
guess = div(last + first, 2)
guess(actual, first, last)
end
defp guess(actual, first, last) when actual < div(last + first, 2) do
IO.puts "Is it #{div(last + first, 2)}"
guess(actual, first, div(last + first, 2))
end
defp guess(actual, first, last) when actual > div(last + first, 2) do
IO.puts "Is it #{div(last + first, 2)}"
guess(actual, div(last + first, 2), last)
end
defp guess(actual, first, last) do
IO.puts actual
end
end
Chop.guess(273, 1..1000)

26
ModulesAndFunctions-7.exs Normal file
View file

@ -0,0 +1,26 @@
# Find the library functions to do the following, and then use each in IEx.
# (If the word Elixir or Erlang appears at the end of the challenge, then youll find the answer in that set of libraries.)
# - Convert a float to a string with two decimal digits. (Erlang)
# - Get the value of an operating-system environment variable. (Elixir)
# - Return the extension component of a file name (so return .exs if given "dave/test.exs"). (Elixir)
# - Return the processs current working directory. (Elixir)
# - Convert a string containing JSON into Elixir data structures. (Just find; dont install.)
# - Execute a command in your operating systems shell.
# https://erldocs.com/current/erts/erlang.html?i=4&search=float%20to#float_to_list/2
IO.puts :erlang.float_to_list(1234.5678, [decimals: 2])
# https://erldocs.com/current/kernel/os.html?i=1&search=os:get#getenv/1
IO.inspect :os.getenv('HOME')
# https://hexdocs.pm/elixir/Path.html#extname/1
IO.puts Path.extname('dave/test.exs')
# https://hexdocs.pm/elixir/System.html#cwd/0
IO.puts System.cwd()
# https://github.com/devinus/poison
# Poison.Parser.parse!(~s({"name": "Devin Torres", "age": 27}), %{})
# https://hexdocs.pm/elixir/System.html#cmd/3
IO.inspect System.cmd("echo", ["hello world"], into: IO.stream(:stdio, :line))

26
PatternMatching-1,2,3.exs Normal file
View file

@ -0,0 +1,26 @@
# Exercise: PatternMatching-1
# Which of the following will match?
# a = [1, 2, 3] Will
# a = 4 Will
# 4 = a Will
# [a, b] = [ 1, 2, 3 ] Won't
# a = [ [ 1, 2, 3 ] ] Will
# [a] = [ [ 1, 2, 3 ] ] Will
# [[a]] = [ [ 1, 2, 3 ] ] Won't
# Exercise: PatternMatching-2
# Which of the following will match?
#
# [ a, b, a ] = [ 1, 2, 3 ] Won't
# [ a, b, a ] = [ 1, 1, 2 ] Won't
# [ a, b, a ] = [ 1, 2, 1 ] Will
#
# Exercise: PatternMatching-3
# The variable a is bound to the value 2. Which of the following will match?
#
# [ a, b, a ] = [ 1, 2, 3 ] Won't
# [ a, b, a ] = [ 1, 1, 2 ] Won't
# a = 1 Will
# ^a = 2 Will
# ^a = 1 Won't
# ^a = 2 - a Won't