From 892d71670945253d983960e0f7ab5cf991660469 Mon Sep 17 00:00:00 2001 From: Wojciech Nagrodzki <278594+wnagrodzki@users.noreply.github.com> Date: Sun, 13 Jan 2019 11:13:35 +0100 Subject: [PATCH] Solutions for chapters 1-10 --- Functions-1.exs | 23 ++++++++++ Functions-2.exs | 15 ++++++ Functions-3.exs | 22 +++++++++ Functions-4.exs | 19 ++++++++ Functions-5.exs | 7 +++ ListsAndRecursion-1.exs | 22 +++++++++ ListsAndRecursion-2.exs | 25 ++++++++++ ListsAndRecursion-3.exs | 25 ++++++++++ ListsAndRecursion-4.exs | 16 +++++++ ListsAndRecursion-5.exs | 86 +++++++++++++++++++++++++++++++++++ ListsAndRecursion-6.exs | 20 ++++++++ ListsAndRecursion-7.exs | 34 ++++++++++++++ ListsAndRecursion-8.exs | 51 +++++++++++++++++++++ ModulesAndFunctions-1,2,3.exs | 27 +++++++++++ ModulesAndFunctions-4.exs | 17 +++++++ ModulesAndFunctions-5.exs | 16 +++++++ ModulesAndFunctions-6.exs | 47 +++++++++++++++++++ ModulesAndFunctions-7.exs | 26 +++++++++++ PatternMatching-1,2,3.exs | 26 +++++++++++ 19 files changed, 524 insertions(+) create mode 100644 Functions-1.exs create mode 100644 Functions-2.exs create mode 100644 Functions-3.exs create mode 100644 Functions-4.exs create mode 100644 Functions-5.exs create mode 100644 ListsAndRecursion-1.exs create mode 100644 ListsAndRecursion-2.exs create mode 100644 ListsAndRecursion-3.exs create mode 100644 ListsAndRecursion-4.exs create mode 100644 ListsAndRecursion-5.exs create mode 100644 ListsAndRecursion-6.exs create mode 100644 ListsAndRecursion-7.exs create mode 100644 ListsAndRecursion-8.exs create mode 100644 ModulesAndFunctions-1,2,3.exs create mode 100644 ModulesAndFunctions-4.exs create mode 100644 ModulesAndFunctions-5.exs create mode 100644 ModulesAndFunctions-6.exs create mode 100644 ModulesAndFunctions-7.exs create mode 100644 PatternMatching-1,2,3.exs diff --git a/Functions-1.exs b/Functions-1.exs new file mode 100644 index 0000000..d7418d1 --- /dev/null +++ b/Functions-1.exs @@ -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}) \ No newline at end of file diff --git a/Functions-2.exs b/Functions-2.exs new file mode 100644 index 0000000..abe77fa --- /dev/null +++ b/Functions-2.exs @@ -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 haven’t 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}) diff --git a/Functions-3.exs b/Functions-3.exs new file mode 100644 index 0000000..1495cd7 --- /dev/null +++ b/Functions-3.exs @@ -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) diff --git a/Functions-4.exs b/Functions-4.exs new file mode 100644 index 0000000..f46827e --- /dev/null +++ b/Functions-4.exs @@ -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 +#   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") diff --git a/Functions-5.exs b/Functions-5.exs new file mode 100644 index 0000000..fc29dfc --- /dev/null +++ b/Functions-5.exs @@ -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) \ No newline at end of file diff --git a/ListsAndRecursion-1.exs b/ListsAndRecursion-1.exs new file mode 100644 index 0000000..8ed8282 --- /dev/null +++ b/ListsAndRecursion-1.exs @@ -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) diff --git a/ListsAndRecursion-2.exs b/ListsAndRecursion-2.exs new file mode 100644 index 0000000..f5c0c0c --- /dev/null +++ b/ListsAndRecursion-2.exs @@ -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]) diff --git a/ListsAndRecursion-3.exs b/ListsAndRecursion-3.exs new file mode 100644 index 0000000..bb2071a --- /dev/null +++ b/ListsAndRecursion-3.exs @@ -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) diff --git a/ListsAndRecursion-4.exs b/ListsAndRecursion-4.exs new file mode 100644 index 0000000..f1c67ad --- /dev/null +++ b/ListsAndRecursion-4.exs @@ -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) diff --git a/ListsAndRecursion-5.exs b/ListsAndRecursion-5.exs new file mode 100644 index 0000000..0a443f7 --- /dev/null +++ b/ListsAndRecursion-5.exs @@ -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 diff --git a/ListsAndRecursion-6.exs b/ListsAndRecursion-6.exs new file mode 100644 index 0000000..c58fcdf --- /dev/null +++ b/ListsAndRecursion-6.exs @@ -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]]]]) diff --git a/ListsAndRecursion-7.exs b/ListsAndRecursion-7.exs new file mode 100644 index 0000000..aa7a502 --- /dev/null +++ b/ListsAndRecursion-7.exs @@ -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) diff --git a/ListsAndRecursion-8.exs b/ListsAndRecursion-8.exs new file mode 100644 index 0000000..48fe166 --- /dev/null +++ b/ListsAndRecursion-8.exs @@ -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 ] +# Here’s 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, there’s 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) diff --git a/ModulesAndFunctions-1,2,3.exs b/ModulesAndFunctions-1,2,3.exs new file mode 100644 index 0000000..2f843c5 --- /dev/null +++ b/ModulesAndFunctions-1,2,3.exs @@ -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) diff --git a/ModulesAndFunctions-4.exs b/ModulesAndFunctions-4.exs new file mode 100644 index 0000000..aa2da45 --- /dev/null +++ b/ModulesAndFunctions-4.exs @@ -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. +# You’ll 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) \ No newline at end of file diff --git a/ModulesAndFunctions-5.exs b/ModulesAndFunctions-5.exs new file mode 100644 index 0000000..3e65eb5 --- /dev/null +++ b/ModulesAndFunctions-5.exs @@ -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; it’s 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) diff --git a/ModulesAndFunctions-6.exs b/ModulesAndFunctions-6.exs new file mode 100644 index 0000000..6a18e3b --- /dev/null +++ b/ModulesAndFunctions-6.exs @@ -0,0 +1,47 @@ +# I’m 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) \ No newline at end of file diff --git a/ModulesAndFunctions-7.exs b/ModulesAndFunctions-7.exs new file mode 100644 index 0000000..d16b9be --- /dev/null +++ b/ModulesAndFunctions-7.exs @@ -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 you’ll 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 process’s current working directory. (Elixir) +# - Convert a string containing JSON into Elixir data structures. (Just find; don’t install.) +# - Execute a command in your operating system’s 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)) \ No newline at end of file diff --git a/PatternMatching-1,2,3.exs b/PatternMatching-1,2,3.exs new file mode 100644 index 0000000..c703731 --- /dev/null +++ b/PatternMatching-1,2,3.exs @@ -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 \ No newline at end of file