diff --git a/exercises/anagram.livemd b/exercises/anagram.livemd index 99d40a44d..af3db1a5c 100644 --- a/exercises/anagram.livemd +++ b/exercises/anagram.livemd @@ -67,6 +67,7 @@ defmodule Anagram do true """ def anagram?(string1, string2) do + String.to_charlist(string1) |> Enum.sort() == String.to_charlist(string2) |> Enum.sort() end @doc """ @@ -84,6 +85,7 @@ defmodule Anagram do [] """ def filter_anagrams(word_list, anagram) do + Enum.filter(word_list, fn word -> anagram?(word, anagram) end) end end ``` diff --git a/exercises/animal_generator.livemd b/exercises/animal_generator.livemd index 81d96f88c..12d9cc543 100644 --- a/exercises/animal_generator.livemd +++ b/exercises/animal_generator.livemd @@ -57,7 +57,13 @@ end Enter your solution below. ```elixir +names = ["Clifford", "Zoboomafoo", "Leonardo"] +animal_types = ["dog", "lemur", "turtle"] +ages = 1..14 +for name <- names, animal_type <- animal_types, age <- ages do + %{name: name, animal_type: animal_type, age: age} +end ``` ## Mark As Completed diff --git a/exercises/battle_map.livemd b/exercises/battle_map.livemd index 2a1b783fc..cfd4781cc 100644 --- a/exercises/battle_map.livemd +++ b/exercises/battle_map.livemd @@ -130,6 +130,22 @@ defprotocol Character do def can_attack?(character, origin, target) end +defimpl Character, for: Barbarian do + def can_attack?(_character, {x1, y1}, {x2, y2}) do + # can move +/- 2 spaces along the x axis -> horizontal + # can move +/- 2 spaces along the y axis -> vertical + # horizontal # vertical + abs(x2 - x1) <= 2 or abs(y2 - y1) <= 2 + end +end + +defimpl Character, for: Wizard do + def can_attack?(_character, {x1, y1}, {x2, y2}) do + # vertical/horizontal # diagonal + x1 == x2 || y1 == y2 || abs(x2 - x1) == abs(y2 - y1) + end +end + ExUnit.start(auto_run: false) defmodule CharacterTests do @@ -198,7 +214,22 @@ true Implement your custom character below. ```elixir +defmodule Archer do + defstruct [] +end +``` + +```elixir +defimpl Character, for: Archer do + def can_attack?(_character, {x1, y1}, {x2, y2}) do + # can move +/- 3 spaces along the x axis -> horizontal + # can move +/- 3 spaces along the y axis -> vertical + # horizontal # vertical + abs(x2 - x1) == 3 or abs(y2 - y1) == 3 + end +end +Character.can_attack?(%Archer{}, {4, 4}, {8, 8}) ``` ## Mark As Completed diff --git a/exercises/book_search.livemd b/exercises/book_search.livemd index a63beca18..9c76e8eaa 100644 --- a/exercises/book_search.livemd +++ b/exercises/book_search.livemd @@ -30,6 +30,7 @@ defmodule Book do iex> %Book{title: "My Book Title"} %Book{title: "My Book Title"} """ + defstruct [:title] @doc """ Search a list of Book structs. Search should match any book that includes the @@ -38,6 +39,7 @@ defmodule Book do ## Examples Include books that exactly match the search query. + # Map.get(map, :title) == query iex> book1 = %Book{title: "A"} iex> book2 = %Book{title: "B"} @@ -46,6 +48,8 @@ defmodule Book do [%Book{title: "A"}] Include books that partially match the search query. + # does the query match any letter of the title? + # use String.contains(book, query) iex> Book.search([%Book{title: "ABC"}], "A") [%Book{title: "ABC"}] @@ -54,15 +58,36 @@ defmodule Book do [%Book{title: "BAC"}] Search should be case insensitive. + # lowercase the input and title iex> Book.search([%Book{title: "ABC"}], "a") [%Book{title: "ABC"}] """ + + # def fuzzy_match(string, char) do + # mysplit = String.split(string, "", trim: true) + # Enum.filter(mysplit, fn element -> element == char end) && string + # end + def search(books, query) do + # books that match one letter + Enum.filter(books, fn book -> String.contains?(book, query) end) + + # books that exactly match + Enum.filter(books, fn book -> Map.get(book, :title) end) end end ``` +```elixir +book = "ABC" +query = "D" +String.contains?(book, query) + +test = %{mybook: "mytitle"} +Map.get(test, ) +``` + ## Mark As Completed diff --git a/exercises/caesar_cypher.livemd b/exercises/caesar_cypher.livemd index 91df9252d..2066ca8fc 100644 --- a/exercises/caesar_cypher.livemd +++ b/exercises/caesar_cypher.livemd @@ -82,13 +82,13 @@ defmodule CaesarCypher do ## Examples - iex> CaesarCypher.encode("abcdefghijklmnopqrstuvwxyz") - "bcdefghijklmnopqrstuvwxyza" + # iex> CaesarCypher.encode("abcdefghijklmnopqrstuvwxyz") + # "bcdefghijklmnopqrstuvwxyza" - Encoding should work on any string + # Encoding should work on any string - iex> CaesarCypher.encode("hello") - "ifmmp" + # iex> CaesarCypher.encode("hello") + # "ifmmp" """ def encode(string) do end @@ -99,14 +99,14 @@ defmodule CaesarCypher do ## Examples - iex> CaesarCypher.encode("abcdefghijklmnopqrstuvwxyz", 1) - "bcdefghijklmnopqrstuvwxyza" + # iex> CaesarCypher.encode("abcdefghijklmnopqrstuvwxyz", 1) + # "bcdefghijklmnopqrstuvwxyza" - iex> CaesarCypher.encode("abcdefghijklmnopqrstuvwxyz", 2) - "cdefghijklmnopqrstuvwxyzab" + # iex> CaesarCypher.encode("abcdefghijklmnopqrstuvwxyz", 2) + # "cdefghijklmnopqrstuvwxyzab" - iex> CaesarCypher.encode("abcdefghijklmnopqrstuvwxyz", 14) - "opqrstuvwxyzabcdefghijklmn" + # iex> CaesarCypher.encode("abcdefghijklmnopqrstuvwxyz", 14) + # "opqrstuvwxyzabcdefghijklmn" Encoding should work on any string. @@ -118,6 +118,25 @@ defmodule CaesarCypher do end ``` +```elixir +string = "abcdefghijklmnopqrstuvwxyz" +offset = 2 + +chars = String.to_charlist(string) + +Enum.map(chars, fn char -> char + 2 end) + +?z - offset + +# Enum.map(chars, fn char -> +# if char < 'z' - offset do +# 'z' +# else +# char + 2 +# end +# end) +``` + ## Mark As Completed diff --git a/exercises/counting_votes.livemd b/exercises/counting_votes.livemd index b7ca899b0..cb1e0c25e 100644 --- a/exercises/counting_votes.livemd +++ b/exercises/counting_votes.livemd @@ -80,6 +80,7 @@ defmodule Votes do 0 """ def count(votes, vote) do + Enum.count(Enum.filter(votes, fn item -> item == vote end)) end end ``` @@ -120,6 +121,10 @@ defmodule VoterTally do %{dog: 2, cat: 3, bird: 1} """ def tally(votes) do + dogs = Enum.count(Enum.filter(votes, fn item -> item == :dog end)) + cats = Enum.count(Enum.filter(votes, fn item -> item == :cat end)) + birds = Enum.count(Enum.filter(votes, fn item -> item == :bird end)) + %{dog: dogs, cat: cats, bird: birds} end end ``` diff --git a/exercises/custom_enum_with_reduce.livemd b/exercises/custom_enum_with_reduce.livemd index 108de363c..7bdee0bad 100644 --- a/exercises/custom_enum_with_reduce.livemd +++ b/exercises/custom_enum_with_reduce.livemd @@ -78,6 +78,11 @@ defmodule CustomEnum do [7, 6, 5, 4] """ def reverse(list) do + # put first element on head of new list + Enum.reduce(list, [], fn element, acc -> + # IO.inspect(binding()) + [element | acc] + end) end @doc """ @@ -92,6 +97,11 @@ defmodule CustomEnum do [true, true, true] """ def map(list, callback_function) do + # while list not empty apply fn to element + Enum.reduce(list, [], fn element, acc -> + [callback_function.(element) | acc] + end) + |> reverse() end @doc """ @@ -107,6 +117,15 @@ defmodule CustomEnum do ["2", "3"] """ def filter(list, callback_function) do + # add element to new list if callback_function(element) returns true + Enum.reduce(list, [], fn element, acc -> + if callback_function.(element) do + [element | acc] + else + acc + end + end) + |> reverse() end @doc """ @@ -117,10 +136,12 @@ defmodule CustomEnum do iex> CustomEnum.sum([1, 2, 3]) 6 - iex> CustomEnum.sum([1, 1, 1]) - 3 + # iex> CustomEnum.sum([1, 1, 1]) + # 3 """ def sum(list_of_integers) do + # add each int to accumulator and return accumulator + Enum.reduce(list_of_integers, 0, fn int, acc -> int + acc end) end @doc """ @@ -135,6 +156,8 @@ defmodule CustomEnum do "Hello, World!" """ def join(list_of_strings) do + # concat each string to acc string + Enum.reduce(list_of_strings, "", fn string, acc -> acc <> string end) end end ``` diff --git a/exercises/dominoes.livemd b/exercises/dominoes.livemd index aa3337209..ac4cc4f8c 100644 --- a/exercises/dominoes.livemd +++ b/exercises/dominoes.livemd @@ -97,7 +97,22 @@ Supervisor.start_link(children, strategy: :rest_for_one) Keep in mind, if you have already started a named process, the supervisor might crash when you attempt to start it again. Re-evaluate the cell after the livebook crashes to resolve this issue. ```elixir +children = [ + %{ + id: D1, + start: {Domino, :start_link, [[name: D1]]} + }, + %{ + id: D2, + start: {Domino, :start_link, [[name: D2]]} + }, + %{ + id: D3, + start: {Domino, :start_link, [[name: D3]]} + } +] +{:ok, pid} = Supervisor.start_link(children, strategy: :rest_for_one) ``` Send your dominos messages to ensure they are crashing in the correct order. They will log a message that demonstrates the `Domino.start_link/1` function was called again. @@ -116,7 +131,7 @@ Process.send(:domino_name, :fall, []) Test sending each `Domino` process a message individually. ```elixir - +Process.send(D3, :fall, []) ``` ## Mark As Completed diff --git a/exercises/drill-patternmatching-replace-nils.livemd b/exercises/drill-patternmatching-replace-nils.livemd index 3ae0a4a58..e34dcb7a3 100644 --- a/exercises/drill-patternmatching-replace-nils.livemd +++ b/exercises/drill-patternmatching-replace-nils.livemd @@ -63,9 +63,39 @@ defmodule ReplaceNils do @doc """ replace nil values in the first list with values from the second list in the same position. + + iex> input1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + iex> input2 = [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil] + iex> ReplaceNils.replace(input1, input2) + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + + iex> input1 = [nil, nil, nil, nil, nil, nil, nil, nil, nil, nil] + iex> input2 = [:a, :b, :c, :d, :e, :f, :g, :h, :i, :j] + iex> ReplaceNils.replace(input1, input2) + [:a, :b, :c, :d, :e, :f, :g, :h, :i, :j] + + iex> input1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + iex> input2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + iex> ReplaceNils.replace(input1, input2) + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + + iex> input1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + iex> input2 = [:a, :b, :c, :d, :e, :f, :g, :h, :i, :j] + iex> ReplaceNils.replace(input1, input2) + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + + iex> input1 = [1, 2, 3, nil, nil, 6, 7, nil, 9, 10] + iex> input2 = [:a, :b, :c, :d, :e, :f, :g, :h, :i, :j] + iex> ReplaceNils.replace(input1, input2) + [1, 2, 3, :d, :e, 6, 7, :h, 9, 10] + + """ def replace(input1, input2) do - nil + Enum.zip_with(input1, input2, fn + nil, y -> y + x, _ -> x + end) end end ``` diff --git a/exercises/email_validation.livemd b/exercises/email_validation.livemd index b885a48c8..002175697 100644 --- a/exercises/email_validation.livemd +++ b/exercises/email_validation.livemd @@ -82,17 +82,59 @@ defmodule Email do iex> Email.valid?("string.string") false - iex> Email.valid?("string@string") - false + # iex> Email.valid?("string@string") + # false - iex> Email.valid?("string@string.") - false + # iex> Email.valid?("string@string.") + # false """ def valid?(email) do + email_regex = ~r/ + \w+ # user + @ # @ + \w+ # host + [.] # . + \w+ # domain + /x + + Regex.match?(email_regex, email) end end ``` +```elixir +good_string = "mail@mail.com" +bad_string = "mail.com" + +email_regex = ~r/ + \w+ # user + @ # @ + \w+ # host + [.] # . + \w+ # domain + /x + +Regex.scan(email_regex, good_string) + +IO.puts(Regex.source(email_regex)) +``` + +```elixir +good_number = "1-123-123-1234" + +number_regex = ~r/ +\d{1} # country code +- # dash +\d{3} # area code +- # dash +\d{3} # exchange +- # dash +\d{4} # circuit number +/x + +Regex.scan(number_regex, good_number) +``` + ## Mark As Completed diff --git a/exercises/fibonacci.livemd b/exercises/fibonacci.livemd index 47b39202c..5453ed3a5 100644 --- a/exercises/fibonacci.livemd +++ b/exercises/fibonacci.livemd @@ -89,7 +89,11 @@ defmodule Fibonacci do iex> Fibonacci.of(20) 6765 """ + def of(0), do: 0 + def of(1), do: 1 + def of(n) do + of(n - 1) + of(n - 2) end end ``` diff --git a/exercises/file_drills.livemd b/exercises/file_drills.livemd index f2857625f..c57322cd9 100644 --- a/exercises/file_drills.livemd +++ b/exercises/file_drills.livemd @@ -25,67 +25,72 @@ This set of drills is for the [File](../reading/file.livemd) module. Follow the Use [File.ls/1](https://hexdocs.pm/elixir/File.html#ls/1) to list all of the files/folders in the current path. ```elixir - +File.ls() ``` Use [File.ls/1](https://hexdocs.pm/elixir/File.html#ls/1) to list all of the files/folders in the parent directory of the current path. ```elixir - +File.ls("../") ``` Use [File.mkdir/1](https://hexdocs.pm/elixir/File.html#mkdir/1) to create a directory called `drills`. ```elixir - +File.cd("/Users/lotek/Desktop") +File.ls() +File.mkdir("drills") +File.ls() ``` Use [File.dir?/2](https://hexdocs.pm/elixir/File.html#dir?/2) to check that `drills` is a folder. ```elixir - +File.dir?("drills") ``` Use [File.write/3](https://hexdocs.pm/elixir/File.html#write/3) to create an empty file called `drills.txt`. ```elixir - +File.write("drills.txt", "") ``` Use [File.exists?/2](https://hexdocs.pm/elixir/File.html#exists?/2) to check that the `drills.txt` file exists. ```elixir - +File.exists?("drills.txt") ``` Use [File.dir?/2](https://hexdocs.pm/elixir/File.html#dir?/2) to check that `drills.txt` is not a folder. ```elixir - +File.dir?("drills.txt") ``` Use [File.write/3](https://hexdocs.pm/elixir/File.html#write/3) to create a filed called `hello.txt` with the content `"world"`. ```elixir - +File.write("hello.txt", "world") ``` Use [File.read/1](https://hexdocs.pm/elixir/File.html#read/1) to read the content of the `hello.txt` file. ```elixir - +File.read("hello.txt") ``` Use [File.write/3](https://hexdocs.pm/elixir/File.html#write/3) to create an empty file in the `drills` folder you previously created. ```elixir - +File.cd("drills") +File.ls() +File.write("empty.txt", "") ``` Use [File.write/3](https://hexdocs.pm/elixir/File.html#write/3) to create an `error/no_entity.txt` file that should return `{:error, :enoent}` because the `error` folder does not exist. ```elixir - +{:error, :enoent} = File.write("error/no_entity.txt", "") ``` Use [File.write/3](https://hexdocs.pm/elixir/File.html#write/3) to create a file `multi-line.txt` with a multi-line string. @@ -103,19 +108,29 @@ line 5 ``` ```elixir +multiline_string = """ +line 1 +line 2 +line 3 +line 4 +line 5 +""" +File.rm("multi-line.txt") +File.ls() +File.write("multi-line.txt", multiline_string) ``` Use [File.read/1](https://hexdocs.pm/elixir/File.html#read/1) to read `multi-line.txt`. ```elixir - +File.read("multi-line.txt") ``` Use [File.stream!/3](https://hexdocs.pm/elixir/File.html#stream!/3) to read each line of `multi-line.txt` and convert it to a list of lines using [Enum.to_list/1](https://hexdocs.pm/elixir/Enum.html#to_list/1). ```elixir - +File.stream!("multi-line.txt") |> Enum.to_list() ``` Use [File.stream!/3](https://hexdocs.pm/elixir/File.html#stream!/3) and [Stream.filter/2](https://hexdocs.pm/elixir/Stream.html#filter/2) to filter in lines from `multi-line.txt` that contain numbers less than or equal to `3`. @@ -133,13 +148,18 @@ line 3 ``` ```elixir +file = "multi-line.txt" +# bah filter sucks +File.stream!(file) |> Stream.take(3) |> Enum.to_list() ``` Use [File.open/2](https://hexdocs.pm/elixir/File.html#open/2), [IO.binread/2](https://hexdocs.pm/elixir/IO.html#binread/2), and [File.close/1](https://hexdocs.pm/elixir/File.html#close/1) to read the first line of `multi-line.txt`. Print the value. ```elixir - +File.open("multi-line.txt") +|> IO.binread() +|> IO.puts() ``` Use [File.mkdir_p/1](https://hexdocs.pm/elixir/File.html#mkdir_p/1) to create: @@ -149,7 +169,10 @@ Use [File.mkdir_p/1](https://hexdocs.pm/elixir/File.html#mkdir_p/1) to create: * `"parent/sub_c"` ```elixir - +File.ls() +File.mkdir_p("parent/sub_a/") +File.mkdir_p("parent/sub_b/") +File.mkdir_p("parent/sub_c/") ``` Use [File.write!/3](https://hexdocs.pm/elixir/File.html#write!/3) to create six empty files: @@ -162,13 +185,21 @@ Use [File.write!/3](https://hexdocs.pm/elixir/File.html#write!/3) to create six * `"parent/sub_c/file"` ```elixir - +File.write!("parent/sub_a/file.txt", "") +File.write!("parent/sub_a/file", "") +File.write!("parent/sub_b/file.txt", "") +File.write!("parent/sub_b/file", "") +File.write!("parent/sub_c/file.txt", "") +File.write!("parent/sub_c/file", "") ``` Use [File.ls!/1](https://hexdocs.pm/elixir/File.html#ls!/1) to find all of the files/folders inside of the `parent` folder. ```elixir - +File.ls("parent") +File.ls("parent/sub_a") +File.ls("parent/sub_b") +File.ls("parent/sub_c") ``` ## Path @@ -176,43 +207,46 @@ Use [File.ls!/1](https://hexdocs.pm/elixir/File.html#ls!/1) to find all of the f Use [Path.join/2](https://hexdocs.pm/elixir/Path.html#join/2) to join `"/parent/"` and `"/child/"` ```elixir - +File.ls() +File.mkdir("child") +File.ls() +Path.join("/parent/", "/child/") ``` Use [Path.join/2](https://hexdocs.pm/elixir/Path.html#join/2) to join `"parent"` and `"child"` ```elixir - +Path.join("parent", "child") ``` Use [Path.join/2](https://hexdocs.pm/elixir/Path.html#join/2) to join `"folder"` and `"file.txt"`. ```elixir - +Path.join("folder", "file.txt") ``` Use [Path.absname/1](https://hexdocs.pm/elixir/Path.html#absname/1) to convert the current path `"."` to an absolute path. ```elixir - +Path.absname(".") ``` Use [Path.dirname/1](https://hexdocs.pm/elixir/Path.html#dirname/1) to find the directory name of `"folder/subfolder/file.txt"` ```elixir - +Path.dirname("folder/subfolder/file.txt") ``` Use [Path.dirname/1](https://hexdocs.pm/elixir/Path.html#dirname/1) to find the directory name of `"file.txt"`. ```elixir - +Path.dirname("file.txt") ``` Use [Path.wildcard/2](https://hexdocs.pm/elixir/Path.html#wildcard/2) to find all files in a nested folder `"parent/*"` that end in a `.txt` extension. You should see your three `file.txt` files created earlier. ```elixir - +Path.wildcard("parent/*/*.txt") ``` Use [File.rm_rf/1](https://hexdocs.pm/elixir/File.html#rm_rf/1) to delete all folders created by this exercise. @@ -222,7 +256,11 @@ CAUTION: DO NOT DELETE IMPORTANT FILES ON YOUR COMPUTER. ```elixir - +File.ls() +File.rm_rf("parent") +File.ls() +File.rm_rf("child") +File.ls() ``` Use [File.rm/1](https://hexdocs.pm/elixir/File.html#rm/1) to delete any remaining files created by this exercise. @@ -232,7 +270,9 @@ CAUTION: DO NOT DELETE IMPORTANT FILES ON YOUR COMPUTER. ```elixir - +File.rm("empty.txt") +File.rm("multi-line.txt") +File.ls() ``` ## Mark As Completed diff --git a/exercises/filter_values_by_type.livemd b/exercises/filter_values_by_type.livemd index 2b9025019..1092bfeb9 100644 --- a/exercises/filter_values_by_type.livemd +++ b/exercises/filter_values_by_type.livemd @@ -82,7 +82,10 @@ defmodule Filter do iex> Filter.integers([1, 2, %{}, {}, []]) [1, 2] """ + + # filter list using is_integer def integers(list) do + Enum.filter(list, fn element -> is_integer(element) end) end @doc """ @@ -94,6 +97,7 @@ defmodule Filter do [1.2, 3.2] """ def floats(list) do + Enum.filter(list, fn element -> is_float(element) end) end @doc """ @@ -105,6 +109,7 @@ defmodule Filter do [1, 2, 1.2, 3.2] """ def numbers(list) do + Enum.filter(list, fn element -> is_integer(element) or is_float(element) end) end @doc """ @@ -116,6 +121,7 @@ defmodule Filter do [:first_atom, :second_atom] """ def atoms(list) do + Enum.filter(list, fn element -> is_atom(element) end) end @doc """ @@ -127,6 +133,7 @@ defmodule Filter do [[1, 2], [4, 5, 6]] """ def lists(list) do + Enum.filter(list, fn element -> is_list(element) && element != [] end) end @doc """ @@ -138,6 +145,7 @@ defmodule Filter do [%{}, %{key: "value"}] """ def maps(list) do + Enum.filter(list, fn element -> is_map(element) end) end @doc """ @@ -149,6 +157,7 @@ defmodule Filter do [[], [key: "value"]] """ def keyword_lists(list) do + Enum.filter(list, fn element -> Keyword.keyword?(element) end) end end ``` diff --git a/exercises/fizzbuzz.livemd b/exercises/fizzbuzz.livemd index 179fe2c17..a1393a308 100644 --- a/exercises/fizzbuzz.livemd +++ b/exercises/fizzbuzz.livemd @@ -77,8 +77,18 @@ defmodule FizzBuzz do ["buzz", 11, "fizz", 13, 14, "fizzbuzz"] """ def run(range) do + Enum.map(range, fn int -> + cond do + rem(int, 15) == 0 -> "fizzbuzz" + rem(int, 3) == 0 -> "fizz" + rem(int, 5) == 0 -> "buzz" + true -> int + end + end) end end + +FizzBuzz.run(1..15) ``` ## Mark As Completed diff --git a/exercises/itinerary.livemd b/exercises/itinerary.livemd index ae7a980ca..396207755 100644 --- a/exercises/itinerary.livemd +++ b/exercises/itinerary.livemd @@ -58,10 +58,21 @@ defmodule Itinerary do """ def has_time?(start, finish, minutes) do + start = DateTime.to_unix(start) + finish = DateTime.to_unix(finish) + + available_time = finish - start + task_time = minutes * 60 + + task_time <= available_time end end ``` +```elixir +DateTime.utc_now() |> DateTime.to_unix() +``` + ## Mark As Completed diff --git a/exercises/lucas_numbers.livemd b/exercises/lucas_numbers.livemd index 658be16ab..b19d965f8 100644 --- a/exercises/lucas_numbers.livemd +++ b/exercises/lucas_numbers.livemd @@ -117,7 +117,11 @@ defmodule Lucas do iex> Lucas.number(20) 15127 """ + def number(0), do: 2 + def number(1), do: 1 + def number(n) do + number(n - 1) + number(n - 2) end @doc """ @@ -142,10 +146,16 @@ defmodule Lucas do """ def sequence(length) do + Enum.map(0..(length - 1), fn elem -> number(elem) end) end end ``` +```elixir +# [2, 1, 3, 4] +[] +``` + ## Mark As Completed diff --git a/exercises/mailbox_server.livemd b/exercises/mailbox_server.livemd index bc5c0bb81..42feb40aa 100644 --- a/exercises/mailbox_server.livemd +++ b/exercises/mailbox_server.livemd @@ -96,7 +96,11 @@ defmodule Mailbox do iex> :sys.get_state(pid) ["Welcome to your mailbox!"] """ - def start_link(_opts) do + + ##### CLIENT ##### + + def start_link(opts) do + GenServer.start_link(__MODULE__, opts) end @doc """ @@ -110,7 +114,8 @@ defmodule Mailbox do iex> :sys.get_state(pid) ["Message 2", "Message 1"] """ - def send(mailbox_pid, message) do + def send(pid, message) do + GenServer.cast(pid, {:mail, message}) end @doc """ @@ -122,7 +127,8 @@ defmodule Mailbox do iex> Mailbox.all_messages(pid) [] """ - def all_messages(mailbox_pid) do + def all_messages(pid) do + GenServer.call(pid, :all_messages) end @doc """ @@ -132,8 +138,12 @@ defmodule Mailbox do iex> {:ok, _pid} = GenServer.start_link(Mailbox, []) """ + + ##### SERVER ##### + @impl true - def init(state) do + def init(args) do + {:ok, args} end @doc """ @@ -145,8 +155,10 @@ defmodule Mailbox do iex> GenServer.call(pid, :all_messages) ["Welcome"] """ + @impl true def handle_call(:all_messages, _from, state) do + {:reply, state, state} end @doc """ @@ -160,8 +172,10 @@ defmodule Mailbox do iex> :sys.get_state(pid) ["Message 2", "Message 1"] """ + @impl true def handle_cast({:mail, mail}, state) do + {:noreply, [mail | state]} end @doc """ @@ -175,8 +189,10 @@ defmodule Mailbox do iex> :sys.get_state(pid) ["Message 2", "Message 1"] """ + @impl true def handle_info({:mail, mail}, state) do + {:noreply, [mail | state]} end end ``` diff --git a/exercises/math_with_guards.livemd b/exercises/math_with_guards.livemd index 901a3f2f8..4ffd85a57 100644 --- a/exercises/math_with_guards.livemd +++ b/exercises/math_with_guards.livemd @@ -122,7 +122,16 @@ defmodule Math do iex> Math.add(%{}, %{}) ** (FunctionClauseError) no function clause matching in Math.add/2 """ - def add(value1, value2) do + def add(value1, value2) when is_integer(value1) and is_integer(value2) do + value1 + value2 + end + + def add(value1, value2) when is_binary(value1) and is_binary(value2) do + value1 <> value2 + end + + def add(value1, value2) when is_list(value1) and is_list(value2) do + value1 ++ value2 end @doc """ @@ -161,12 +170,26 @@ defmodule Math do iex> Math.subtract(%{}, %{}) ** (FunctionClauseError) no function clause matching in Math.subtract/2 """ - def subtract(value1, value2) do + def subtract(value1, value2) when is_integer(value1) and is_integer(value2) do + value1 - value2 + end + + def subtract(value1, value2) when is_binary(value1) and is_binary(value2) do + (String.split(value1, "", trim: true) -- String.split(value2, "", trim: true)) + |> List.to_string() + end + + def subtract(value1, value2) when is_list(value1) and is_list(value2) do + value1 -- value2 end end ``` -## Mark As Completed +```elixir +string1 = String.split("abcd", "", trim: true) +string2 = String.split("abc", "", trim: true) +List.to_string(string1 -- string2) +``` diff --git a/exercises/math_with_protocols.livemd b/exercises/math_with_protocols.livemd index 68e4b771d..2ac0eb158 100644 --- a/exercises/math_with_protocols.livemd +++ b/exercises/math_with_protocols.livemd @@ -116,9 +116,9 @@ defprotocol Math do iex> Math.add(4, 4) 8 - Math.add([1, 2], [3, 4]) + iex> Math.add([1, 2], [3, 4]) [1, 2, 3, 4] - Math.add([1, 2, 3], [4, 5, 6]) + iex> Math.add([1, 2, 3], [4, 5, 6]) [1, 2, 3, 4, 5, 6] iex> Math.add("abc", "def") @@ -132,6 +132,7 @@ defprotocol Math do iex> Math.add({}, {}) ** (Protocol.UndefinedError) protocol Math not implemented for {} of type Tuple """ + def add(value1, value2) @doc """ @@ -162,10 +163,45 @@ defprotocol Math do """ def subtract(value1, value2) end + +defimpl Math, for: Integer do + def add(value1, value2) do + value1 + value2 + end + + def subtract(value1, value2) do + value1 - value2 + end +end + +defimpl Math, for: List do + def add(value1, value2) do + value1 ++ value2 + end + + def subtract(value1, value2) do + value1 -- value2 + end +end + +defimpl Math, for: BitString do + def add(value1, value2) do + value1 <> value2 + end + + def subtract(value1, value2) do + (String.graphemes(value1) -- String.graphemes(value2)) + |> List.to_string() + end +end ``` ## Mark As Completed +```elixir +String.graphemes("abc") +``` + ```elixir diff --git a/exercises/message_validation.livemd b/exercises/message_validation.livemd index bf26498b1..dddb82fdc 100644 --- a/exercises/message_validation.livemd +++ b/exercises/message_validation.livemd @@ -93,13 +93,13 @@ defmodule Message do iex> %Message{} %Message{body: nil} """ - defstruct [] + defstruct [:body] @doc """ Send messages between users. Returns a string of the message if provided valid input. - ## Examples + ## Examples iex> Message.send("hello!") "hello!" @@ -116,19 +116,31 @@ defmodule Message do iex> Message.send(123) ** (FunctionClauseError) no function clause matching in Message.send/1 + iex> Message.send(%{}) ** (FunctionClauseError) no function clause matching in Message.send/1 + iex> Message.send({}) ** (FunctionClauseError) no function clause matching in Message.send/1 + iex> Message.send(%Message{body: nil}) ** (FunctionClauseError) no function clause matching in Message.send/1 - + + iex> Message.send(%Message{body: {}}) ** (FunctionClauseError) no function clause matching in Message.send/1 + """ - def send(message) do + # sender sends message + def send(message) when is_binary(message) do + message + end + + # receiver sends back message body + def send(message) when is_binary(message.body) do + message.body end end ``` diff --git a/exercises/monster_spawner.livemd b/exercises/monster_spawner.livemd index 8dc7d564c..f8beb8f5d 100644 --- a/exercises/monster_spawner.livemd +++ b/exercises/monster_spawner.livemd @@ -5,7 +5,7 @@ Mix.install([ {:jason, "~> 1.4"}, {:kino, "~> 0.8.0", override: true}, {:youtube, github: "brooklinjazz/youtube"}, - {:hidden_cell, github: "brooklinjazz/hidden_cell"}, + {:hidden_cell, github: "brooklinjazz/hidden_cell"} ]) ``` @@ -104,6 +104,8 @@ Implement the `Monster` process as documented below. defmodule Monster do use GenServer + ##### CLIENT ##### + @doc """ Start the `Monster` process. @@ -111,9 +113,11 @@ defmodule Monster do iex> {:ok, pid} = Monster.start_link([]) """ + def start_link(opts) do # IO.inspect/2 to observe when a `Monster` process starts. IO.inspect(opts, label: "Monster Started") + GenServer.start_link(__MODULE__, opts) end @doc """ @@ -121,13 +125,14 @@ defmodule Monster do ## Examples - iex> {:ok, pid} = Monster.start_link([]) - iex> :sys.get_state(pid) - %{health: 100} - iex> Monster.attack(pid, 30) - iex> :sys.get_state(pid) - %{health: 70} + # iex> {:ok, pid} = Monster.start_link([]) + # iex> :sys.get_state(pid) + # %{health: 100} + # iex> Monster.attack(pid, 30) + # iex> :sys.get_state(pid) + # %{health: 70} """ + def attack(monster_pid, amount) do end @@ -140,9 +145,13 @@ defmodule Monster do iex> Monster.health(pid) 100 """ + def health(monster_pid) do + GenServer.call(monster_pid, :health) end + ##### SERVER ##### + @doc """ Callback function to start the `Monster` process. Monsters should start with a `:health` value in a map. @@ -153,8 +162,10 @@ defmodule Monster do iex> :sys.get_state(pid) %{health: 100} """ + @impl true def init(_opts) do + {:ok, %{health: 100}} end @doc """ @@ -164,14 +175,14 @@ defmodule Monster do ## Examples - iex> {:ok, pid} = GenServer.start_link(Monster, []) - iex> :sys.get_state(pid) - %{health: 100} - iex> GenServer.cast(pid, {:attack, 20}) - iex> :sys.get_state(pid) - %{health: 80} - iex> GenServer.cast(pid, {:attack, 80}) - ** (RuntimeError) dying! + # iex> {:ok, pid} = GenServer.start_link(Monster, []) + # iex> :sys.get_state(pid) + # %{health: 100} + # iex> GenServer.cast(pid, {:attack, 20}) + # iex> :sys.get_state(pid) + # %{health: 80} + # iex> GenServer.cast(pid, {:attack, 80}) + # ** (RuntimeError) dying! """ @impl true def handle_cast({:attack, damage}, state) do @@ -188,10 +199,18 @@ defmodule Monster do """ @impl true def handle_call(:health, _from, state) do + # IO.inspect(state) + health = Map.get(state, :health) + {:reply, health, state} end end ``` +```elixir +test = %{health: 100} +Map.get(test, :health) +``` + ## Supervisor Create three named `Monster` processes under a single supervisor. When one `Monster` process dies after its health reaches zero, another should be restarted in it's place. diff --git a/exercises/named_number_lists.livemd b/exercises/named_number_lists.livemd index 7b1f06cba..ac6e70d07 100644 --- a/exercises/named_number_lists.livemd +++ b/exercises/named_number_lists.livemd @@ -75,7 +75,22 @@ flowchart Enter your solution below. ```elixir - +rando = Enum.map(1..10, fn _ -> Enum.random(0..9) end) + +Enum.map(rando, fn int -> + case int do + 0 -> "zero" + 1 -> "one" + 2 -> "two" + 3 -> "three" + 4 -> "four" + 5 -> "five" + 6 -> "six" + 7 -> "seven" + 8 -> "eight" + 9 -> "nine" + end +end) ``` ## Mark As Completed diff --git a/exercises/naming_numbers.livemd b/exercises/naming_numbers.livemd index df121b16f..24319112b 100644 --- a/exercises/naming_numbers.livemd +++ b/exercises/naming_numbers.livemd @@ -72,7 +72,22 @@ naming_numbers.(1) Enter your solution below. ```elixir +naming_numbers = fn integer -> + case integer do + 0 -> "zero" + 1 -> "one" + 2 -> "two" + 3 -> "three" + 4 -> "four" + 5 -> "five" + 6 -> "six" + 7 -> "seven" + 8 -> "eight" + 9 -> "nine" + end +end +naming_numbers.(1) ``` ## Numbering Names @@ -169,7 +184,22 @@ flowchart ```elixir +numbering_names = fn int_str -> + case String.downcase(int_str) do + "zero" -> 0 + "one" -> 1 + "two" -> 2 + "three" -> 3 + "four" -> 4 + "five" -> 5 + "six" -> 6 + "seven" -> 7 + "eight" -> 8 + "nine" -> 9 + end +end +numbering_names.("Nine") ``` ## Mark As Completed diff --git a/exercises/number_finder.livemd b/exercises/number_finder.livemd index 8a9a5a859..05f9cb9d0 100644 --- a/exercises/number_finder.livemd +++ b/exercises/number_finder.livemd @@ -37,12 +37,21 @@ defmodule NumberFinder do iex> NumberFinder.smallest([2, 3, 1]) 1 + iex> NumberFinder.smallest([2, 2, 3, 4]) 2 + iex> NumberFinder.smallest([2, 2, 3, 4, 10, 20, -3]) -3 """ def smallest(number_list) do + Enum.reduce(number_list, fn elem, acc -> + if acc < elem do + acc + else + elem + end + end) end @doc """ @@ -52,12 +61,21 @@ defmodule NumberFinder do iex> NumberFinder.largest([2, 3, 1]) 3 + iex> NumberFinder.largest([2, 2, 3, 4, 4]) 4 + iex> NumberFinder.largest([2, 2, 3, 4, 10, 20, -3]) 20 """ def largest(number_list) do + Enum.reduce(number_list, fn elem, acc -> + if acc > elem do + acc + else + elem + end + end) end end ``` diff --git a/exercises/palindrome.livemd b/exercises/palindrome.livemd index a6137c0ba..7a3b1c845 100644 --- a/exercises/palindrome.livemd +++ b/exercises/palindrome.livemd @@ -70,6 +70,7 @@ defmodule Palindrome do false """ def palindrome?(string) do + string == String.reverse(string) end end ``` diff --git a/exercises/process_drills.livemd b/exercises/process_drills.livemd index f39a778ce..b0085a09d 100644 --- a/exercises/process_drills.livemd +++ b/exercises/process_drills.livemd @@ -25,13 +25,21 @@ This set of drills is for the [Process](https://hexdocs.pm/elixir/Process.html) Use [Process.send/3](https://hexdocs.pm/elixir/Process.html#send/3) and `self()` to send the process for the Elixir cell below a `:message` message. Use [receive](https://hexdocs.pm/elixir/Kernel.SpecialForms.html#receive/1) to receive the message in the same cell. ```elixir +send(self(), "message") +receive do + "message" -> "received" +end ``` Use [Process.send/3](https://hexdocs.pm/elixir/Process.html#send/3) and `self()` to send the process for the Elixir cell below a message with a value i.e. `{:message, "value"}`. Use [receive](https://hexdocs.pm/elixir/Kernel.SpecialForms.html#receive/1) to receive the message in the same cell and return the value. ```elixir +send(self(), {:message, "value"}) +receive do + {:message, "value"} -> "value" +end ``` ## Process.spawn/2 @@ -39,31 +47,47 @@ Use [Process.send/3](https://hexdocs.pm/elixir/Process.html#send/3) and `self()` Use [Process.spawn/2](https://hexdocs.pm/elixir/Process.html#spawn/2) to spawn a new process which adds two integers together. ```elixir - +spawn(fn -> 1 + 2 end) ``` Use [Process.spawn/2](https://hexdocs.pm/elixir/Process.html#spawn/2) and [Process.sleep/1](https://hexdocs.pm/elixir/Process.html#sleep/1) to spawn a process that sleeps for five seconds, then prints "Finished!". ```elixir - +pid = spawn(fn -> 1 + 2 end) +Process.sleep(5000) +Process.alive?(pid) || IO.puts("Finished!") ``` Use [Process.spawn/2](https://hexdocs.pm/elixir/Process.html#spawn/2) and [receive](https://hexdocs.pm/elixir/Kernel.SpecialForms.html#receive/1) to spawn a process that receives a `:message` message. Use [Process.send/3](https://hexdocs.pm/elixir/Process.html#send/3) to send the spawned process a `:message` message. The spawned process should print `"received a message!"`. ```elixir +pid1 = + spawn(fn -> + receive do + :message -> IO.puts("received a message!") + end + end) +send(pid1, :message) ``` Use [Process.spawn/2](https://hexdocs.pm/elixir/Process.html#spawn/2) and [receive](https://hexdocs.pm/elixir/Kernel.SpecialForms.html#receive/1) to spawn a process that receives a message with a value i.e. `{:message, "value"}`. Use [Process.send/3](https://hexdocs.pm/elixir/Process.html#send/3) to send the spawned process a message with a value. The spawned process should print the received value. ```elixir +pid2 = + spawn(fn -> + receive do + {:message, value} -> IO.puts("#{value}") + end + end) +send(pid2, "Hello World!") ``` Use [Process.spawn/2](https://hexdocs.pm/elixir/Process.html#spawn/2) to spawn a process that raises an error. Notice it does not crash the Livebook, because it is an **unlinked** process. ```elixir - +pid3 = spawn(fn -> raise "oops" end) ``` Use [Process.spawn/3](https://hexdocs.pm/elixir/Process.html#spawn/3) and [Process.sleep/1](https://hexdocs.pm/elixir/Process.html#sleep/1) to spawn a process which raises an error after one second. Use [Process.link/1](https://hexdocs.pm/elixir/Process.html#link/1) to link the process. @@ -71,7 +95,8 @@ Use [Process.spawn/3](https://hexdocs.pm/elixir/Process.html#spawn/3) and [Proce Livebook should crash. Comment out your solution so that you can move on. ```elixir - +pid4 = spawn(fn -> raise "oops" end) +Process.sleep(1000) ``` ## Process.alive?/1 @@ -79,7 +104,11 @@ Livebook should crash. Comment out your solution so that you can move on. Use [Process.spawn/2](https://hexdocs.pm/elixir/Process.html#spawn/2) and [Process.sleep/1](https://hexdocs.pm/elixir/Process.html#sleep/1) to spawn a process that sleeps for five seconds. Use [Process.alive?/1](https://hexdocs.pm/elixir/Process.html#alive?/1) and [Process.sleep/1](https://hexdocs.pm/elixir/Process.html#sleep/1) to check if the process is alive after two seconds. [Process.alive?/1](https://hexdocs.pm/elixir/Process.html#alive?/1) should return `true`. ```elixir - +pid4 = spawn(fn -> true end) +Process.sleep(2000) +Process.alive?(pid4) || "still alive" +Process.sleep(3000) +Process.alive?(pid4) || "it's dead, Jim" ``` Use [Process.spawn/2](https://hexdocs.pm/elixir/Process.html#spawn/2) and [Process.sleep/1](https://hexdocs.pm/elixir/Process.html#sleep/1) to spawn a process that sleeps for five seconds. Use [Process.alive?/1](https://hexdocs.pm/elixir/Process.html#alive?/1) and [Process.sleep/1](https://hexdocs.pm/elixir/Process.html#sleep/1) to check if the process is alive after six seconds. [Process.alive?/1](https://hexdocs.pm/elixir/Process.html#alive?/1) should return `false`. diff --git a/exercises/rock_paper_scissors.livemd b/exercises/rock_paper_scissors.livemd index 0cfd2820c..7b8073b0d 100644 --- a/exercises/rock_paper_scissors.livemd +++ b/exercises/rock_paper_scissors.livemd @@ -54,7 +54,15 @@ Then, return the winning choice of either `:rock`, `:paper`, or `:scissors` that Enter your solution below. ```elixir +player_choice = Enum.random([:rock, :paper, :scissors]) + +IO.puts(player_choice) +case player_choice do + :rock -> :paper + :paper -> :scissors + :scissors -> :rock +end ``` ## Create Two Player Rock Paper Scissors @@ -107,7 +115,21 @@ Bind a `player1_choice` and `player2_choice` variable to `:rock`, `:paper`, or ` Enter your solution below. ```elixir - +player1 = Enum.random([:rock, :paper, :scissors]) +IO.inspect(player1: player1) + +player2 = Enum.random([:rock, :paper, :scissors]) +IO.inspect(player2: player2) + +case {player1, player2} do + {:rock, :scissors} -> "Player 1 Wins!" + {:paper, :rock} -> "Player 1 Wins!" + {:scissors, :paper} -> "Player 1 Wins!" + {:rock, :paper} -> "Player 2 Wins!" + {:paper, :scissors} -> "Player 2 Wins!" + {:scissors, :rock} -> "Player 2 Wins!" + {same, same} -> "Draw" +end ``` ## Mark As Completed diff --git a/exercises/rock_paper_scissors_lizard_spock.livemd b/exercises/rock_paper_scissors_lizard_spock.livemd index 400a7af02..e924cdd3f 100644 --- a/exercises/rock_paper_scissors_lizard_spock.livemd +++ b/exercises/rock_paper_scissors_lizard_spock.livemd @@ -83,6 +83,17 @@ defmodule RockPaperScissorsLizardSpock do false """ def beats?(guess1, guess2) do + {guess1, guess2} in [ + {:rock, :scissors}, + {:rock, :lizard}, + {:paper, :rock}, + {:paper, :spock}, + {:scissors, :paper}, + {:scissors, :lizards}, + {:lizard, :paper}, + {:lizard, :spock}, + {:spock, :scissors} + ] end @doc """ @@ -99,9 +110,20 @@ defmodule RockPaperScissorsLizardSpock do iex> RockPaperScissorsLizardSpock.play(:lizard, :lizard) "Player 2 Wins!" """ + def play(player1, player2) do + cond do + beats?(player1, player2) -> "Player 1 Wins!" + not beats?(player1, player2) -> "Player 2 Wins!" + end end end + +RockPaperScissorsLizardSpock.play(:rock, :paper) +``` + +```elixir + ``` ## Mark As Completed diff --git a/exercises/rpg_dialogue.livemd b/exercises/rpg_dialogue.livemd index dd17f3caf..7006b4ffd 100644 --- a/exercises/rpg_dialogue.livemd +++ b/exercises/rpg_dialogue.livemd @@ -82,7 +82,9 @@ defmodule Character do iex> %Character{name: "Frodo"} ** (ArgumentError) the following keys must also be given when building struct Character: [:name] """ - defstruct [] + @enforced_key [:name] + + defstruct @enforced_key ++ [:class, :weapon] @doc """ Introduce the character by name. @@ -95,7 +97,8 @@ defmodule Character do iex> Character.introduce(%Character{name: "Aragorn"}) "My name is Aragorn." """ - def introduce(character) do + def introduce(char) do + "My name is #{char.name}." end @doc """ @@ -109,7 +112,8 @@ defmodule Character do iex> Character.attack(%Character{name: "Aragorn", weapon: "sword"}) "I attack with my sword!" """ - def attack(character) do + def attack(char) do + "I attack with my #{char.weapon}!" end @doc """ @@ -123,7 +127,8 @@ defmodule Character do iex> Character.class(%Character{name: "Aragorn", class: "ranger"}) "I am a ranger." """ - def class(character) do + def class(char) do + "I am a #{char.class}." end @doc """ @@ -137,9 +142,14 @@ defmodule Character do iex> Character.war_cry(%Character{name: "Aragorn", class: "ranger"}) "My name is Aragorn and I am a ranger!" """ - def war_cry(character) do + def war_cry(char) do + "My name is #{char.name} and I am a #{char.class}!" end + # def war_cry2(name, class) do + # "My name is #{name} and I am a #{class}" + # end + @doc """ Declare that one character has defeated another. @@ -151,9 +161,31 @@ defmodule Character do iex> Character.defeat(%Character{name: "Aragorn"}, %Character{name: "Gimli", class: "warrior"}) "My name is Aragorn and I have defeated the warrior Gimli!" """ - def defeat(character1, character2) do + def defeat(char1, char2) do + "My name is #{char1.name} and I have defeated the #{char2.class} #{char2.name}!" end end + +# defmodule Test do +# import Character + +# def test do +# aragorn = %Character{name: "Aragorn", class: "ranger", weapon: "sword"} +# gandalf = %Character{name: "Gandalf", class: "wizard", weapon: "staff"} +# Character.introduce(aragorn) +# Character.attack(aragorn) +# Character.class(aragorn) +# Character.war_cry(aragorn) +# Character.war_cry2(aragorn.name, aragorn.class) +# Character.defeat(aragorn, gandalf) +# end +# end + +# Test.test +``` + +```elixir + ``` ### Bonus: Character Instances diff --git a/exercises/save_game.livemd b/exercises/save_game.livemd index 3c0c5c5e5..bcce80873 100644 --- a/exercises/save_game.livemd +++ b/exercises/save_game.livemd @@ -58,12 +58,14 @@ defmodule Game do Save an elixir term into a given file name. """ def save(data, filename) do + File.write!(filename, :erlang.term_to_binary(data)) end @doc """ Retrieve an elixir term from a given file name. """ def load(filename) do + File.read!(filename) |> :erlang.binary_to_term() end end ``` diff --git a/exercises/score_tracker.livemd b/exercises/score_tracker.livemd index 83ecbf897..11b84bf46 100644 --- a/exercises/score_tracker.livemd +++ b/exercises/score_tracker.livemd @@ -87,7 +87,11 @@ defmodule ScoreTracker do iex> {:ok, pid} = ScoreTracker.start_link([]) """ - def start_link(_opts) do + + ##### CLIENT ##### + + def start_link(opts) do + GenServer.start_link(__MODULE__, opts) end @doc """ @@ -99,7 +103,9 @@ defmodule ScoreTracker do iex> ScoreTracker.score(pid, 10) :ok """ - def score(score_tracker_pid, amount) do + + def score(pid, amount) do + GenServer.cast(pid, {:score, amount}) end @doc """ @@ -111,7 +117,26 @@ defmodule ScoreTracker do iex> ScoreTracker.get_score(pid) 0 """ - def get_score(score_tracker_pid) do + + def get_score(pid) do + GenServer.call(pid, :get_score) + end + + ##### SERVER ##### + + @impl true + def init(_args) do + {:ok, 0} + end + + @impl true + def handle_cast({:score, amount}, state) do + {:noreply, state + amount} + end + + @impl true + def handle_call(:get_score, _from, state) do + {:reply, state, state} end end ``` @@ -183,7 +208,11 @@ defmodule MultiplayerScoreTracker do iex> {:ok, pid} = MultiplayerScoreTracker.start_link([]) """ + + ##### CLIENT ##### + def start_link(_opts) do + GenServer.start_link(__MODULE__, []) end @doc """ @@ -199,7 +228,8 @@ defmodule MultiplayerScoreTracker do iex> MultiplayerScoreTracker.score(pid, :abc, 10) :ok """ - def score(multiplayer_score_tracker_pid, player_name, amount) do + def score(pid, player_name, amount) do + GenServer.cast(pid, {:score, player_name, amount}) end @doc """ @@ -229,7 +259,8 @@ defmodule MultiplayerScoreTracker do iex> MultiplayerScoreTracker.all_scores(pid) %{player1: 10, player2: 10} """ - def all_scores(multiplayer_score_tracker_pid) do + def all_scores(pid) do + GenServer.call(pid, :all_scores) end @doc """ @@ -239,18 +270,41 @@ defmodule MultiplayerScoreTracker do Player does not exist. - iex> {:ok, pid} = MultiplayerScoreTracker.start_link([]) - iex> MultiplayerScoreTracker.get_score(pid, :player1) - nil + iex> {:ok, pid} = MultiplayerScoreTracker.start_link([]) + iex> MultiplayerScoreTracker.get_score(pid, :player1) + nil - Player exists. + Player exists. - iex> {:ok, pid} = MultiplayerScoreTracker.start_link([]) - iex> MultiplayerScoreTracker.score(pid, :abc, 10) - iex> MultiplayerScoreTracker.get_score(pid, :abc) - 10 + iex> {:ok, pid} = MultiplayerScoreTracker.start_link([]) + iex> MultiplayerScoreTracker.score(pid, :abc, 10) + iex> MultiplayerScoreTracker.get_score(pid, :abc) + 10 """ - def get_score(multiplayer_score_tracker_pid, player_name) do + def get_score(pid, player_name) do + GenServer.call(pid, {:get_score, player_name}) + end + + ##### SERVER ##### + + @impl true + def init(_args) do + {:ok, %{}} + end + + @impl true + def handle_cast({:score, player_name, amount}, state) do + {:noreply, Map.update(state, player_name, amount, fn state -> state + amount end)} + end + + @impl true + def handle_call(:all_scores, _from, state) do + {:reply, state, state} + end + + @impl true + def handle_call({:get_score, player_name}, _from, state) do + {:reply, state[player_name], state} end end ``` diff --git a/exercises/stack.livemd b/exercises/stack.livemd index 994c997c7..a1f54438b 100644 --- a/exercises/stack.livemd +++ b/exercises/stack.livemd @@ -58,13 +58,34 @@ Copy the code below into your `Stack` module. defmodule Stack do use GenServer - def start_link(_opts) do + # client functions + def start_link(opts) do + GenServer.start_link(__MODULE__, opts) end def push(pid, element) do + GenServer.call(pid, {:push, element}) end def pop(pid) do + GenServer.call(pid, :pop) + end + + # server functions + def init(opts) do + {:ok, opts} + end + + def handle_call({:push, element}, _from, state) do + {:reply, [element | state], [element | state]} + end + + def handle_call(:pop, _from, state) do + if Enum.empty?(state) do + {:reply, nil, nil} + else + {:reply, hd(state), tl(state)} + end end end ``` @@ -72,26 +93,65 @@ end Then copy the following code into the associated test file for the `Stack` module. ```elixir +ExUnit.start(auto_run: false) + defmodule StackTest do use ExUnit.Case describe "start_link/1" do - test "with no configuration" - test "with a default state" + test "initial state with no configuration" do + {:ok, pid} = Stack.start_link([]) + assert :sys.get_state(pid) == [] + end + + test "initial state with a default state" do + {:ok, pid} = Stack.start_link([1]) + assert :sys.get_state(pid) == [1] + end end describe "push/2" do - test "an element onto an empty stack" - test "an element onto a stack with one element" - test "an element onto a stack with multiple elements" + test "push an element onto an empty stack" do + {:ok, pid} = Stack.start_link([]) + Stack.push(pid, 3) + assert :sys.get_state(pid) == [3] + end + + test "push an element onto a stack with one element" do + {:ok, pid} = Stack.start_link([1]) + Stack.push(pid, 2) + assert :sys.get_state(pid) == [2, 1] + end + + test "push an element onto a stack with multiple elements" do + {:ok, pid} = Stack.start_link([2, 1]) + Stack.push(pid, 3) + assert :sys.get_state(pid) == [3, 2, 1] + end end describe "pop/1" do - test "an empty stack" - test "a stack with one element" - test "a stack with multiple elements" + test "pop an empty stack" do + {:ok, pid} = Stack.start_link([]) + assert Stack.pop(pid) == nil + assert :sys.get_state(pid) == nil + end + + test "pop a stack with one element" do + {:ok, pid} = Stack.start_link([1]) + assert Stack.pop(pid) == 1 + assert :sys.get_state(pid) == [] + end + + test "pop a stack with multiple elements" do + {:ok, pid} = Stack.start_link([3, 2, 1]) + assert Stack.pop(pid) == 3 + assert :sys.get_state(pid) == [2, 1] + end end end + +ExUnit.run() ``` ## Bonus: GitHub Repository diff --git a/exercises/stack_server.livemd b/exercises/stack_server.livemd index 8eee8346f..d4a6fc09c 100644 --- a/exercises/stack_server.livemd +++ b/exercises/stack_server.livemd @@ -93,95 +93,44 @@ Consider starting with the `handle_call/3` callback functions, then implement th defmodule Stack do use GenServer - @doc """ - Start the `Stack` process. - - ## Examples - - iex> {:ok, pid} = Stack.start_link([]) - """ - def start_link(_opts) do + # Client API (Behaviour) + def start_link(opts) do + # IO.inspect("start_link", label: "Client") + # Note: second arg (opts) is what's passed to init + GenServer.start_link(__MODULE__, opts) end - @doc """ - Synchronously push an element onto the top of the `Stack`. Return the current stack. - - ## Examples - - iex> {:ok, pid} = Stack.start_link([]) - iex> Stack.push(pid, 1) - [1] - iex> Stack.push(pid, 2) - [2, 1] - iex> Stack.push(pid, 3) - [3, 2, 1] - """ - def push(stack_pid, element) do + def push(pid, element) do + # IO.inspect("push #{element}", label: "Client") + GenServer.call(pid, {:push, element}) end - @doc """ - Pop an element from the top of the `Stack`. - - ## Examples - - iex> {:ok, pid} = Stack.start_link([]) - iex> Stack.push(pid, 1) - iex> Stack.push(pid, 2) - iex> Stack.push(pid, 3) - iex> Stack.pop(pid) - 3 - iex> Stack.pop(pid) - 2 - iex> Stack.pop(pid) - 1 - """ - def pop(stack_pid) do + def pop(pid) do + # IO.inspect("pop", label: "Client") + GenServer.call(pid, :pop) end - @doc """ - Necessary callback function to start the `Stack` process. - - ## Examples - - iex> {:ok, pid} = GenServer.start_link(Stack, []) - """ - def init(_opts) do + # Server API (Implementation) + @impl true + def init(init_args) do + # IO.inspect("init", label: "Server") + {:ok, init_args} end - @doc """ - Callback function to add an element onto the top of the `Stack`. - - ## Examples - - iex> {:ok, pid} = GenServer.start_link(Stack, []) - iex> GenServer.call(pid, {:push, 1}) - [1] - iex> GenServer.call(pid, {:push, 2}) - [2, 1] - iex> GenServer.call(pid, {:push, 3}) - [3, 2, 1] - """ + @impl true def handle_call({:push, element}, _from, state) do + # IO.inspect("handle_call :push #{element}", label: "Server") + new_state = [element | state] + response = new_state + {:reply, response, new_state} end - @doc """ - Callback function to pop an element off of the top of the `Stack`. - You do not need to handle popping when a stack is empty. - - ## Examples - - iex> {:ok, pid} = GenServer.start_link(Stack, []) - iex> GenServer.call(pid, {:push, 1}) - iex> GenServer.call(pid, {:push, 2}) - iex> GenServer.call(pid, {:push, 3}) - iex> GenServer.call(pid, :pop) - 3 - iex> GenServer.call(pid, :pop) - 2 - iex> GenServer.call(pid, :pop) - 1 - """ + @impl true def handle_call(:pop, _from, state) do + # IO.inspect("handle_call :pop", label: "Server") + new_state = tl(state) + response = new_state + {:reply, response, new_state} end end ``` diff --git a/exercises/supervised_stack.livemd b/exercises/supervised_stack.livemd index b6db113b5..59fe5303a 100644 --- a/exercises/supervised_stack.livemd +++ b/exercises/supervised_stack.livemd @@ -104,7 +104,14 @@ children = [ Keep in mind, if you have already started a supervisor with the `Stack` process, your livebook may crash. You can resolve this issue by simply re-running the cell below to start the supervisor again. ```elixir +children = [ + %{ + id: Stack, + start: {Stack, :start_link, [[]]} + } +] +{:ok, pid} = Supervisor.start_link(children, strategy: :one_for_one) ``` You should be able to send a `:pop` message to the `Stack` process and the [Supervisor](https://hexdocs.pm/elixir/Supervisor.html) will restart the `Stack` process. @@ -112,7 +119,7 @@ You should be able to send a `:pop` message to the `Stack` process and the [Supe Uncomment and evaluate the code below to test your supervisor. ```elixir -# GenServer.call(Stack, :pop) +GenServer.call(Stack, :pop) ``` ## Mark As Completed diff --git a/exercises/task_drills.livemd b/exercises/task_drills.livemd index 900020104..fc9340b52 100644 --- a/exercises/task_drills.livemd +++ b/exercises/task_drills.livemd @@ -25,19 +25,27 @@ This set of drills is for [Tasks](../reading/task.livemd). Follow the instructio Use [Task.async/1](https://hexdocs.pm/elixir/Task.html#async/1) and [Task.await/2](https://hexdocs.pm/elixir/Task.html#await/2) to spawn a task process that returns any response. ```elixir +Kino.Process.render_seq_trace(fn -> + Process.sleep(2000) + Task.async(fn -> IO.puts("hello world!") end) +end) +# task = Task.async(fn -> Process.sleep(2000) end) +# Task.await(task) ``` Use [Task.async/1](https://hexdocs.pm/elixir/Task.html#async/1) and [Task.await/2](https://hexdocs.pm/elixir/Task.html#await/2) to spawn a task process that sleeps for six seconds. It should cause a timeout error. ```elixir - +task = Task.async(fn -> Process.sleep(6000) end) +Task.await(task) ``` Use [Task.async/1](https://hexdocs.pm/elixir/Task.html#async/1) and [Task.await/2](https://hexdocs.pm/elixir/Task.html#await/2) to spawn a task process that sleeps for six seconds. Provide a timeout value to [Task.await/2](https://hexdocs.pm/elixir/Task.html#await/2) so that it properly awaits the response. ```elixir - +task = Task.async(fn -> Process.sleep(2000) end) +Task.await(task, 7000) ``` ## Task.yield/2 @@ -55,7 +63,7 @@ time ``` ```elixir -{time, _result} = +Kino.Process.render_seq_trace(fn -> :timer.tc(fn -> # 1000 tasks # 1000 operations -> parallel @@ -69,9 +77,25 @@ time Task.await_many(tasks) end) +end) -10_009_225 -1_001_494 +# {time, _result} = +# :timer.tc(fn -> +# # 1000 tasks +# # 1000 operations -> parallel +# tasks = +# Enum.map(1..10, fn int -> +# Task.async(fn -> +# IO.puts("starting task #{int}") +# Process.sleep(1000) +# end) +# end) + +# Task.await_many(tasks) +# end) + +# 10_009_225 +# 1_001_494 ``` ### Fast Operation @@ -83,31 +107,55 @@ time ``` ```elixir -{time, _result} = +Kino.Process.render_seq_trace(fn -> :timer.tc(fn -> # 1000 tasks # 1000 operations -> parallel tasks = Enum.map(1..10, fn int -> Task.async(fn -> int * 2 end) end) Task.await_many(tasks) end) +end) + +# {time, _result} = +# :timer.tc(fn -> +# # 1000 tasks +# # 1000 operations -> parallel +# tasks = Enum.map(1..10, fn int -> Task.async(fn -> int * 2 end) end) +# Task.await_many(tasks) +# end) ``` Use [Task.async/1](https://hexdocs.pm/elixir/Task.html#async/1) and [Task.yield/2](https://hexdocs.pm/elixir/Task.html#yield/2) to spawn a task process that sleeps for six seconds and returns any response. Notice that the yield is `nil`. ```elixir +task = + Task.async(fn -> + Process.sleep(6000) + IO.puts("howdy!") + end) +Task.yield(task) ``` Use [Task.async/1](https://hexdocs.pm/elixir/Task.html#async/1) and [Task.yield/2](https://hexdocs.pm/elixir/Task.html#yield/2) to spawn a task process that sleeps for six seconds and returns any response. Provide a timeout value to [Task.yield/2](https://hexdocs.pm/elixir/Task.html#yield/2) so that it properly returns the response. ```elixir +task = + Task.async(fn -> + Process.sleep(6000) + IO.puts("howdy!") + end) +Task.yield(task, 7000) ``` Use [Task.async/1](https://hexdocs.pm/elixir/Task.html#async/1), [Task.yield/2](https://hexdocs.pm/elixir/Task.html#yield/2), and [Task.shutdown/2](https://hexdocs.pm/elixir/Task.html#shutdown/2) to spawn a task process that sleeps for six seconds. Shutdown the task process if it does not yield any value. ```elixir - +Kino.Process.render_seq_trace(fn -> + task = Task.async(fn -> Process.sleep(6000) end) + Task.yield(task) || Task.shutdown(task) +end) ``` ## Task.await_many/2 @@ -115,25 +163,37 @@ Use [Task.async/1](https://hexdocs.pm/elixir/Task.html#async/1), [Task.yield/2]( Use [Task.async/1](https://hexdocs.pm/elixir/Task.html#async/1) and [Task.await_many/2](https://hexdocs.pm/elixir/Task.html#await_many/2) to spawn two task processes that both return a random number from `1..10`. ```elixir +tasks = [ + Task.async(fn -> :rand.uniform(10) end), + Task.async(fn -> :rand.uniform(10) end) +] +Task.await_many(tasks) ``` Use [Enum.map/2](https://hexdocs.pm/elixir/Enum.html#map/2), [Task.async/1](https://hexdocs.pm/elixir/Task.html#async/1), and [Task.await_many/2](https://hexdocs.pm/elixir/Task.html#await_many/2) to spawn one hundred task processes that all return a random integer from `1..100`. ```elixir - +tasks = Enum.map(1..100, fn _num -> Task.async(fn -> :rand.uniform(100) end) end) +Task.await_many(tasks) ``` Use [Enum.map/2](https://hexdocs.pm/elixir/Enum.html#map/2), [Task.async/1](https://hexdocs.pm/elixir/Task.html#async/1), and [Task.await_many/2](https://hexdocs.pm/elixir/Task.html#await_many/2) to spawn one hundred task processes that return a list doubled numbers from one to one hundred. i.e. `[2, 4, 6, 8, ..., 198, 200]`. ```elixir - +tasks = Enum.map(1..100, fn int -> Task.async(fn -> int * 2 end) end) +Task.await_many(tasks) ``` Use [Enum.map/2](https://hexdocs.pm/elixir/Enum.html#map/2), [Task.async/1](https://hexdocs.pm/elixir/Task.html#async/1), [String.upcase/2](https://hexdocs.pm/elixir/String.html#upcase/2), and [Task.await_many/2](https://hexdocs.pm/elixir/Task.html#await_many/2) to concurrently capitalize every string in the list `["apple", "pear", "peaches"]` ```elixir +tasks = + Enum.map(["apple", "pear", "peaches"], fn string -> + Task.async(fn -> String.capitalize(string) end) + end) +Task.await_many(tasks) ``` ## Task.Supervisor diff --git a/exercises/tic-tac-toe.livemd b/exercises/tic-tac-toe.livemd index 08c12950f..bafb9544f 100644 --- a/exercises/tic-tac-toe.livemd +++ b/exercises/tic-tac-toe.livemd @@ -153,7 +153,13 @@ defmodule TicTacToe do iex> TicTacToe.at(board, {2, 2}) "C" """ + def at(board, coordinate) do + {x, y} = coordinate + + Enum.reverse(board) + |> Enum.at(y) + |> Enum.at(x) end @doc """ @@ -177,6 +183,15 @@ defmodule TicTacToe do [[nil, "X", "O"], [nil, "X", "O"],[nil, nil, "X"]] """ def fill(board, coordinate, symbol) do + {x, y} = coordinate + + row_index = 2 - y + + row = Enum.at(board, row_index) + + new_row = List.replace_at(row, x, symbol) + + List.replace_at(board, row_index, new_row) end end ``` diff --git a/exercises/time_converting.livemd b/exercises/time_converting.livemd index 5346dbbf2..578aeae68 100644 --- a/exercises/time_converting.livemd +++ b/exercises/time_converting.livemd @@ -74,6 +74,12 @@ defmodule TimeConverter do """ def to_seconds(amount, unit) do + case unit do + :seconds -> amount + :minutes -> amount * 60 + :hours -> amount * 3600 + :days -> amount * 86400 + end end @doc """ @@ -82,8 +88,8 @@ defmodule TimeConverter do ## Examples - iex> TimeConverter.from_seconds(1, :seconds) - 1.0 + iex> TimeConverter.from_seconds(2, :seconds) + 2.0 iex> TimeConverter.from_seconds(60, :minutes) 1.0 @@ -96,6 +102,12 @@ defmodule TimeConverter do """ def from_seconds(amount, unit) do + case unit do + :seconds -> amount / 1.0 + :minutes -> amount / 60 + :hours -> amount / 3600 + :days -> amount / 86400 + end end end ``` diff --git a/exercises/timeline.livemd b/exercises/timeline.livemd index 5bb45c981..6cafbd278 100644 --- a/exercises/timeline.livemd +++ b/exercises/timeline.livemd @@ -96,6 +96,14 @@ defmodule Timeline do [9, 12, 2] """ def from_dates(dates) do + # chunk the date list into pairs with offset = 1 + # map over the chunked pairs with Date.diff + + pairs = Enum.chunk_every(dates, 2, 1, :discard) + + Enum.map(pairs, fn [date1, date2] -> + Date.diff(date2, date1) + end) end @doc """ @@ -113,11 +121,31 @@ defmodule Timeline do iex> Timeline.from_strings(["2020-01-01", "2020-01-10", "2020-01-22", "2020-01-24"]) [9, 12, 2] """ + + def make_date_from_string(string) do + [year, month, day] = String.split(string, "-", trim: true) + Date.new!(String.to_integer(year), String.to_integer(month), String.to_integer(day)) + end + def from_strings(date_strings) do + # convert list of strings to new list of dates + # call from_dates with new list of dates + + new_list = Enum.map(date_strings, fn string -> make_date_from_string(string) end) + from_dates(new_list) end end ``` +```elixir +test_string = ["2020-01-01", "2020-01-10", "2020-01-22", "2020-01-24"] + +Enum.map(test_string, fn string -> + [year, month, day] = String.split(string, "-", trim: true) + Date.new!(String.to_integer(year), String.to_integer(month), String.to_integer(day)) +end) +``` + ## Mark As Completed diff --git a/exercises/timer.livemd b/exercises/timer.livemd index c51b45286..e823abdda 100644 --- a/exercises/timer.livemd +++ b/exercises/timer.livemd @@ -76,37 +76,66 @@ end Implement the `Timer` module as documented below. You will also have to implement the necessary [GenServer](https://hexdocs.pm/elixir/GenServer.html) callback functions such as [GenServer.init/1](https://hexdocs.pm/elixir/GenServer.html#init/1), [GenServer.handle_info/2](https://hexdocs.pm/elixir/GenServer.html#handle_info/2), and [GenServer.handle_call/3](https://hexdocs.pm/elixir/GenServer.html#handle_call/3). ```elixir - defmodule Timer do - @moduledoc """ - Documentation for `Timer` - """ - use GenServer - - @doc """ - Start the `Timer` process. - - ## Examples - - iex> {:ok, pid} = Timer.start_link([]) - """ - def start_link(_opts) do - end - - @doc """ - Get the number of seconds that have elapsed since the `Timer` was started. - - ## Examples - - iex> {:ok, pid} = Timer.start_link([]) - iex> Timer.seconds(pid) - 0 - iex> Process.sleep(1200) - iex> Timer.seconds(pid) - 1 - """ - def seconds(timer_pid) do - end - end +defmodule Timer do + @moduledoc """ + Documentation for `Timer` + """ + use GenServer + + @doc """ + Start the `Timer` process. + + ## Examples + + iex> {:ok, pid} = Timer.start_link([]) + """ + + ##### CLIENT ##### + + def start_link(_opts) do + GenServer.start_link(__MODULE__, []) + end + + @doc """ + Get the number of seconds that have elapsed since the `Timer` was started. + + ## Examples + + iex> {:ok, pid} = Timer.start_link([]) + iex> Timer.seconds(pid) + 0 + iex> Process.sleep(1200) + iex> Timer.seconds(pid) + 1 + """ + def seconds(pid) do + GenServer.call(pid, :seconds) + end + + ##### SERVER ##### + + @impl true + def init(_opts) do + increment_timer() + {:ok, 0} + end + + @impl true + def handle_call(:seconds, _from, state) do + {:reply, state, state} + end + + @impl true + def handle_info(:increment, state) do + increment_timer() + {:noreply, state + 1} + end + + defp increment_timer do + # add one second + Process.send_after(self(), :increment, 1000) + end +end ``` ## Bonus :timer @@ -157,16 +186,58 @@ defmodule AlternateTimer do @doc """ Start the `Timer` process. + + ## Examples + + iex> {:ok, pid} = Timer.start_link([]) """ + + ##### CLIENT ##### + def start_link(_opts) do GenServer.start_link(__MODULE__, []) end @doc """ Get the number of seconds that have elapsed since the `Timer` was started. + + ## Examples + + iex> {:ok, pid} = Timer.start_link([]) + iex> Timer.seconds(pid) + 0 + iex> Process.sleep(1200) + iex> Timer.seconds(pid) + 1 + """ - def seconds(timer_pid) do - GenServer.call(timer_pid, :seconds) + + def seconds(pid) do + GenServer.call(pid, :seconds) + end + + ##### SERVER ##### + + @impl true + def init(_opts) do + increment_timer() + {:ok, 0} + end + + @impl true + def handle_call(:seconds, _from, state) do + {:reply, state, state} + end + + @impl true + def handle_info(:increment, state) do + increment_timer() + {:noreply, state + 1} + end + + defp increment_timer do + # add one second + :timer.send_interval(1000, self(), :increment) end end ``` diff --git a/exercises/treasure_matching.livemd b/exercises/treasure_matching.livemd index 8995e1bf2..854e2a4aa 100644 --- a/exercises/treasure_matching.livemd +++ b/exercises/treasure_matching.livemd @@ -27,79 +27,98 @@ jewel Use pattern matching to bind a `jewel` variable to the `"jewel"` string. ```elixir -[1, 2, 3, "jewel"] +[_, _, _, jewel] = [1, 2, 3, "jewel"] +jewel ``` ```elixir -%{key1: "value", key2: "jewel"} +%{key1: value, key2: jewel} = %{key1: "value", key2: "jewel"} +jewel ``` ```elixir -%{1 => "jewel"} +%{1 => jewel} = %{1 => "jewel"} +jewel ``` ```elixir -%{%{key: [1, 2, 3, 4, 5, {}]} => "jewel"} +%{%{key: [1, 2, 3, 4, 5, {}]} => jewel} = %{%{key: [1, 2, 3, 4, 5, {}]} => "jewel"} +jewel ``` ```elixir -%{north: %{south: %{west: %{east: "jewel"}}}} +%{north: %{south: %{west: %{east: jewel}}}} = %{north: %{south: %{west: %{east: "jewel"}}}} +jewel ``` ```elixir -[2, "jewel"] +[_, jewel] = [2, "jewel"] +jewel ``` ```elixir -["jewel", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] +[jewel | tail] = ["jewel", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] +jewel ``` ```elixir -[1, "jewel", 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] +[_, jewel | tail] = [1, "jewel", 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] +jewel ``` ```elixir -[1, 2, "jewel", 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] +[_, _, jewel | tail] = [1, 2, "jewel", 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] +jewel ``` ```elixir -[[], [1, [2, "jewel"]]] +[_, [_, [_ | jewel]]] = [[], [1, [2, "jewel"]]] +jewel ``` ```elixir -"here is the jewel" +"here is the " <> jewel = "here is the jewel" +jewel ``` ```elixir -{"jewel"} +{jewel} = {"jewel"} +jewel ``` ```elixir -{"jewel", 1} +{jewel, _} = {"jewel", 1} +jewel ``` ```elixir -{1, 2, "jewel"} +{_, _, jewel} = {1, 2, "jewel"} +jewel ``` ```elixir -["jewel"] ++ Enum.to_list(1..100) +[jewel | tail] = ["jewel"] ++ Enum.to_list(1..100) +jewel ``` ```elixir -[key: "jewel"] +[key: jewel] = [key: "jewel"] +jewel ``` ```elixir -[south: "jewel", east: {1, 2}] +[{:south, jewel} | tail] = [south: "jewel", east: {1, 2}, north: "two"] +jewel ``` ```elixir -Enum.map(1..4, fn each -> (each == 2 && "jewel") || each end) +[_, jewel | tail] = Enum.map(1..4, fn each -> (each == 2 && "jewel") || each end) +jewel ``` ```elixir -Enum.map(1..4, &((&1 > 3 && "jewel") || &1)) +[_, _, _, jewel] = Enum.map(1..4, &((&1 > 3 && "jewel") || &1)) +jewel ``` ## Mark As Completed diff --git a/exercises/typespec_drills.livemd b/exercises/typespec_drills.livemd index 3ed091780..faaa46eee 100644 --- a/exercises/typespec_drills.livemd +++ b/exercises/typespec_drills.livemd @@ -157,72 +157,89 @@ end ```elixir defmodule FunctionSpecs do + @spec do_nothing :: nil def do_nothing do nil end + @spec accept_and_return_anything(any()) :: any() def accept_and_return_anything(anything) do anything end + @spec double(float()) :: float() def double(float) when is_float(float) do float * 2.0 end + @spec double(integer()) :: integer() def double(integer) when is_integer(integer) do integer * 2 end + @spec double(number()) :: number() def double(number) do number * 2 end + @spec add(integer(), integer()) :: integer() def add(integer1, integer2) do integer1 + integer2 end + @spec multiply(integer(), integer()) :: integer() def multiply(integer1, integer2) do integer1 * integer2 end + @spec divide(integer(), integer()) :: float() def divide(integer1, integer2) do integer1 / integer2 end + @spec rounded_divide(integer(), integer()) :: integer() def rounded_divide(integer1, integer2) do div(integer1, integer2) end + @spec concat(String.t(), String.t()) :: String.t() def concat(string1, string2) do string1 <> string2 end + @spec to_string(integer()) :: String.t() def to_string(integer) do Integer.to_string(integer) end + @spec merge(map(), map()) :: map() def merge(map1, map2) do Map.merge(map1, map2) end + @spec get_or_empty(Keyword.t(), atom()) :: any() def get_or_empty(keyword_list, atom_key) do Keyword.get(keyword_list, atom_key, "") end + @spec split_and_lowercase(String.t()) :: [String.t()] def split_and_lowercase(string) do string |> String.downcase() |> String.split("", trim: true) end + @spec string_to_int(String.t()) :: integer() def string_to_int(string) do String.to_integer(string) end + @spec integers_to_strings([integer()]) :: [String.t()] def integers_to_strings(integers) do Enum.map(integers, fn int -> Integer.to_string(int) end) end + @spec one_to_two(1) :: 2 def one_to_two(1) do 2 end @@ -254,13 +271,13 @@ end ```elixir defmodule CustomTypes do # a string or number - @type unparsed_number + @type unparsed_number :: String.t() | number() # a list of strings - @type strings + @type strings :: [String.t()] # a map with :title (string) and :content (string) keys. - @type book + @type book :: %{title: String.t(), content: String.t()} # A map with :name (string) and `:books` (a list of books) keys. - @type author + @type author :: %{name: String.t(), books: [book()]} end ``` diff --git a/exercises/weighted_voting.livemd b/exercises/weighted_voting.livemd index 96eb35039..166bb852c 100644 --- a/exercises/weighted_voting.livemd +++ b/exercises/weighted_voting.livemd @@ -82,12 +82,21 @@ defmodule WeightedVoting do iex> WeightedVoting.count([dogs: 20, dogs: 10], :dogs) 30 + iex> WeightedVoting.count([cats: 10, dogs: 20, dogs: 30], :dogs) 50 + iex> WeightedVoting.count([cats: 10, dogs: 20, dogs: 10, cats: 30], :cats) 40 """ def count(votes, vote_to_count) do + Enum.reduce(votes, 0, fn {key, value}, acc -> + if key == vote_to_count do + acc + value + else + acc + end + end) end @doc """ @@ -100,16 +109,35 @@ defmodule WeightedVoting do iex> WeightedVoting.tally([dogs: 20, dogs: 10]) [dogs: 30] + iex> WeightedVoting.tally([cats: 10, dogs: 20, dogs: 10]) [cats: 10, dogs: 30] + iex> WeightedVoting.tally([cats: 10, dogs: 20, cats: 20, dogs: 10, birds: 20]) [birds: 20, cats: 30, dogs: 30] """ def tally(votes) do + Enum.reduce(votes, [], fn {animal, value}, acc -> + Keyword.update(acc, animal, value, fn existing_value -> + value + existing_value + end) + end) + |> Enum.sort() end end ``` +```elixir +votes = [cats: 10, dogs: 20, cats: 20, dogs: 10, birds: 20] +vote_to_count = :birds + +def count(votes, vote_to_count) do + Enum.reduce(votes, fn acc, vote_to_count -> + IO.inspect(binding()) + end) +end +``` + ### Bonus: Tally Map Create a `WeightedVoting.tally_map/1` function which returns a map instead of a keyword list. diff --git a/exercises/with_points.livemd b/exercises/with_points.livemd index a6cf47a66..86a33af07 100644 --- a/exercises/with_points.livemd +++ b/exercises/with_points.livemd @@ -73,6 +73,13 @@ end Enter your solution below. +```elixir +%{team1_name: team1, team2_name: team2} +%{round1: %{team1: team1r1, team2: team2r1}} +%{round2: %{team1: team1r2, team2: team2r2}} +%{round3: %{team1: team1r3, team2: team2r3}} +``` + ```elixir defmodule Points do @doc """ @@ -96,15 +103,15 @@ defmodule Points do """ def tally(game) do with %{team1_name: team1, team2_name: team2} <- game, - %{round1: %{team1: t1r1, team2: t2r1}} <- game, - %{round2: %{team1: t1r2, team2: t2r2}} <- game, - %{round3: %{team1: t1r3, team2: t2r3}} <- game do + %{round1: %{team1: team1r1, team2: team2r1}} <- game, + %{round2: %{team1: team1r2, team2: team2r2}} <- game, + %{round3: %{team1: team1r3, team2: team2r3}} <- game do %{} - |> Map.put(team1, t1r1 + t1r2 + t1r3) - |> Map.put(team2, t2r1 + t2r2 + t2r3) + # IO.inspect(binding()) + |> Map.put(team1, team1r1 + team1r2 + team1r3) + |> Map.put(team2, team2r1 + team2r2 + team2r3) else - error -> - {:error, :invalid} + _error -> {:error, :invalid} end end end diff --git a/progress.json b/progress.json index c298c1f19..2c41ab8e5 100644 --- a/progress.json +++ b/progress.json @@ -9,23 +9,23 @@ "mazes_exercise": false, "rollable_expressions_exercise": false, "modules_reading": false, - "file_reading": false, + "file_reading": true, "agents_and_ets_reading": false, "protocols_reading": false, "games_score_tracker_exercise": false, - "habit_tracker_exercise": false, + "habit_tracker_exercise": true, "reduce_reading": false, "code_editors_reading": false, "exdoc_reading": false, "process_drills_exercise": false, "saferange_exercise": false, - "time_converting_exercise": false, + "time_converting_exercise": true, "pokemon_server_exercise": false, "strings_reading": false, "counting_votes_exercise": false, "metric_conversion_exercise": false, "shopping_list_exercise": false, - "file_drills_exercise": false, + "file_drills_exercise": true, "fibonacci_challenge_exercise": false, "ecto_changeset_reading": false, "comparison_operators_reading": false, @@ -43,33 +43,33 @@ "pascals_triangle_exercise": false, "book_search_book_content_reading": false, "portfolio_exercise": false, - "non_enumerables_reading": false, + "non_enumerables_reading": true, "control_flow_reading": false, "lists_vs_tuples_reading": false, - "dominoes_exercise": false, + "dominoes_exercise": true, "command_line_family_tree_exercise": false, "phoenix_and_ecto_reading": false, - "number_finder_exercise": false, + "number_finder_exercise": true, "pic_chat_image_upload_reading": false, - "timeline_exercise": false, + "timeline_exercise": true, "newsletter_reading": false, "advanced_pattern_matching_reading": false, - "games_guessing_game_exercise": false, + "games_guessing_game_exercise": true, "math_with_guards_exercise": false, "pokemon_api_exercise": false, "file_search_exercise": false, - "animal_generator_exercise": false, + "animal_generator_exercise": true, "group_project_blog_exercise": false, "treasure_matching_exercise": false, "github_engineering_journal_exercise": false, - "mailbox_server_exercise": false, + "mailbox_server_exercise": true, "blog_posts_exercise": false, - "git_reading": false, + "git_reading": true, "named_number_lists_exercise": false, "big_o_notation_reading": false, "battle_map_exercise": false, "group_project_blog_presentation_exercise": false, - "timer_exercise": false, + "timer_exercise": true, "games_benchmarking_exercise": false, "guessing_games_exercise": false, "sign_up_form_exercise": false, @@ -77,7 +77,7 @@ "caesar_cypher_exercise": false, "booleans_reading": false, "blog_comments_exercise": false, - "anagram_exercise": false, + "anagram_exercise": true, "lazy_product_filters_exercise": false, "file_system_todo_app_exercise": false, "phoenix_1.7_reading": false, @@ -86,10 +86,10 @@ "github_collab_exercise": false, "web_servers_reading": false, "book_search_deployment_reading": false, - "tic-tac-toe_exercise": false, + "tic-tac-toe_exercise": true, "games_supervised_score_tracker_exercise": false, "benchmarking_reading": false, - "processes_reading": false, + "processes_reading": true, "games_menu_exercise": false, "supervised_mix_project_reading": false, "liveview_reading": false, @@ -97,12 +97,12 @@ "io_reading": false, "tuples_reading": false, "ranges_reading": false, - "task_reading": false, + "task_reading": true, "executables_reading": false, "rdbms_reading": false, "fun_formulas_exercise": false, "rpg_dialogue_exercise": false, - "palindrome_exercise": false, + "palindrome_exercise": true, "comprehensions_reading": false, "comments_reading": false, "rock_paper_scissors_exercise": false, @@ -112,24 +112,24 @@ "blog_seeding_exercise": false, "custom_enum_with_recursion_exercise": false, "drill-patternmatching-replace-nils_exercise": false, - "games_rock_paper_scissors_exercise": false, + "games_rock_paper_scissors_exercise": true, "blog_migration_exercise": false, "inventory_management_exercise": false, "iex_reading": false, "phone_number_parsing_exercise": false, "maps_reading": false, - "exunit_reading": false, + "exunit_reading": true, "keyword_lists_reading": false, "command_line_reading": false, "stack_exercise": false, "traffic_light_server_exercise": false, "supervisor_and_genserver_drills_exercise": false, "computer_hardware_reading": false, - "supervised_stack_exercise": false, + "supervised_stack_exercise": true, "naming_numbers_exercise": false, "deployment_exercise": false, "typespecs_reading": false, - "card_counting_exercise": false, + "card_counting_exercise": true, "math_with_protocols_exercise": false, "stack_server_exercise": false, "pokemon_battle_exercise": false, @@ -141,11 +141,11 @@ "blog_content_exercise": false, "math_module_testing_exercise": false, "phoenix_authentication_reading": false, - "games_setup_exercise": false, + "games_setup_exercise": true, "task_drills_exercise": false, "monster_spawner_exercise": false, "metaprogramming_reading": false, - "start_here_reading": false, + "start_here_reading": true, "games_supervisor_setup_exercise": false, "pic_chat_pub_sub_reading": false, "lists_reading": false, @@ -153,18 +153,18 @@ "functions_reading": false, "spoonacular_recipe_api_exercise": false, "book_changeset_exercise": false, - "weighted_voting_exercise": false, + "weighted_voting_exercise": true, "supervisors_reading": false, "save_game_exercise": false, "document_tools_exercise": false, "factorial_exercise": false, - "livebook_reading": false, - "filter_values_by_type_exercise": false, + "livebook_reading": true, + "filter_values_by_type_exercise": true, "book_search_exercise": false, "guards_reading": false, "sql_drills_exercise": false, "maps_mapsets_keyword_lists_reading": false, - "lucas_numbers_exercise": false, + "lucas_numbers_exercise": true, "mapset_product_filters_exercise": false, "consumable_protocol_exercise": false, "task_supervisor_reading": false, @@ -180,13 +180,13 @@ "with_points_exercise": false, "itinerary_exercise": false, "apis_reading": false, - "built-in_modules_reading": false, + "built-in_modules_reading": true, "rps_pattern_matching_exercise": false, "arithmetic_reading": false, "rps_guards_exercise": false, "book_search_tags_reading": false, "email_validation_exercise": false, - "custom_enum_with_reduce_exercise": false, + "custom_enum_with_reduce_exercise": true, "blog_comment_form_exercise": false, "doctests_reading": false, "pic_chat_infinite_scroll_reading": false, @@ -195,14 +195,14 @@ "strings_and_binaries_reading": false, "phoenix_drills_exercise": false, "html_css_reading": false, - "score_tracker_exercise": false, + "score_tracker_exercise": true, "tailwind_reading": false, "mapset_drills_exercise": false, "book_search_books_reading": false, "recursion_reading": false, "in-memory_todo_list_exercise": false, "capstone_project_guide_reading": false, - "fibonacci_exercise": false, + "fibonacci_exercise": true, "typespec_drills_exercise": false, "custom_assertions_exercise": false, "atoms_reading": false diff --git a/reading/built-in_modules.livemd b/reading/built-in_modules.livemd index 55aeaa455..d574b8f19 100644 --- a/reading/built-in_modules.livemd +++ b/reading/built-in_modules.livemd @@ -108,6 +108,7 @@ Use [Kernel.elem/2](https://hexdocs.pm/elixir/Kernel.html#elem/2) to retrieve `1 ```elixir tuple = {0, 4, 1, 100, 5, 7} +elem(tuple, 3) ``` ## Checking Types @@ -149,31 +150,31 @@ is_atom(:example) ``` ```elixir -%{} +is_map(%{}) ``` ```elixir -{} +is_map({}) ``` ```elixir -[] +is_list([]) ``` ```elixir -true +is_boolean(true) ``` ```elixir -1.0 +is_float(1.0) ``` ```elixir -1 +is_integer(1) ``` ```elixir -"" +is_binary("") ``` The [Kernel](https://hexdocs.pm/elixir/Kernel.html) is reasonably large. Remember, our goal is not to memorize every function but to develop familiarity with repeated practice. @@ -213,8 +214,10 @@ Created a `capped_seconds` variable that uses the value of `seconds` and cannot Enter your solution below. if `seconds` is less than `0` `capped_seconds` should be `0`. If `seconds` is greater than `59`, then `capped_seconds` should be `59`. ```elixir -seconds = 60 -capped_seconds = nil +seconds = 20 +# min(seconds, 59) +# max(seconds, 0) +capped_seconds = max(min(seconds, 59), 0) ``` ## Safe Inspection @@ -264,7 +267,8 @@ map = %{} list = [1, 2, 3] tuple = {1, 2, 3} -"" +# "" +inspect(tuple) ``` ## The Integer Module @@ -345,7 +349,7 @@ Use the [Integer.gcd/2](https://hexdocs.pm/elixir/Integer.html#gcd/2) function t positive integer that divides both 10 and 15 evenly, so the result should be `5`. ```elixir - +Integer.gcd(10, 15) ``` ## The String Module @@ -427,7 +431,7 @@ Use the [String.at/2](https://hexdocs.pm/elixir/String.html#at/2) function to ge ```elixir -"hello" +String.at("hello", 2) ``` Use the [String.at/2](https://hexdocs.pm/elixir/String.html#at/2) function to retrieve the letter `"o"` in `"hello"` @@ -610,7 +614,7 @@ Use [List.delete_at/2](https://hexdocs.pm/elixir/List.html#delete_at/2) to remov ```elixir -[2, 1, 3] +List.delete_at([2, 1, 3], 0) ``` Use [List.flatten/1](https://hexdocs.pm/elixir/List.html#flatten/1) to flatten the following list into `[1, 2, 3, 4, 5, 6, 7, 8, 9]` @@ -626,7 +630,7 @@ Use [List.flatten/1](https://hexdocs.pm/elixir/List.html#flatten/1) to flatten t ```elixir -[1, 2, [3, 4, 5], 6, [7, [8, [9]]]] +List.flatten([1, 2, [3, 4, 5], 6, [7, [8, [9]]]]) ``` Use [List.insert_at/3](https://hexdocs.pm/elixir/List.html#insert_at/3) to insert `2` into the following list to make `[1, 2, 3]`. @@ -642,7 +646,7 @@ Use [List.insert_at/3](https://hexdocs.pm/elixir/List.html#insert_at/3) to inser ```elixir -[1, 3] +List.insert_at([1, 3], 1, 2) ``` Use [List.last/2](https://hexdocs.pm/elixir/List.html#last/2) to retrieve the last element `10000` in a list from `1` to `10000`. @@ -664,7 +668,7 @@ Use [List.last/2](https://hexdocs.pm/elixir/List.html#last/2) to retrieve the la ```elixir -Enum.to_list(1..10000) +List.last(Enum.to_list(1..10000)) ``` Use [List.update_at/3](https://hexdocs.pm/elixir/List.html#update_at/3) to subtract `2` from `4` in the following list to make `[1, 2, 3]`. @@ -680,7 +684,7 @@ Use [List.update_at/3](https://hexdocs.pm/elixir/List.html#update_at/3) to subtr ```elixir -[1, 4, 3] +List.update_at([1, 4, 3], 1, &(&1 - 2)) ``` Use [List.zip/1](https://hexdocs.pm/elixir/List.html#zip/1) to combine these two lists to make `[{"a", 1}, {"b", 2}, {"c", 3}]`. @@ -701,6 +705,7 @@ Use [List.zip/1](https://hexdocs.pm/elixir/List.html#zip/1) to combine these two ```elixir letters = ["a", "b", "c"] numbers = [1, 2, 3] +List.zip([letters, numbers]) ``` ## The Map Module @@ -756,7 +761,7 @@ Use [Map.get/3](https://hexdocs.pm/elixir/Map.html#get/3) to retrieve the `"worl ```elixir -%{hello: "world"} +Map.get(%{hello: "world"}, :hello) ``` Use [Map.put/3](https://hexdocs.pm/elixir/Map.html#put/3) to add the key `:two` with the value `2` to the following map. @@ -772,7 +777,7 @@ Use [Map.put/3](https://hexdocs.pm/elixir/Map.html#put/3) to add the key `:two` ```elixir -%{one: 1} +Map.put(%{one: 1}, :two, 2) ``` Use [Map.keys/1](https://hexdocs.pm/elixir/Map.html#keys/1) to retrieve the keys for the following map. @@ -788,7 +793,7 @@ Use [Map.keys/1](https://hexdocs.pm/elixir/Map.html#keys/1) to retrieve the keys ```elixir -%{key1: 1, key2: 2, key3: 3} +Map.keys(%{key1: 1, key2: 2, key3: 3}) ``` Use [Map.delete/2](https://hexdocs.pm/elixir/Map.html#delete/2) to remove `:key1` from the following map. @@ -804,7 +809,7 @@ Use [Map.delete/2](https://hexdocs.pm/elixir/Map.html#delete/2) to remove `:key1 ```elixir -%{key1: 1, key2: 2, key3: 3} +Map.delete(%{key1: 1, key2: 2, key3: 3}, :key1) ``` Use [Map.merge/2](https://hexdocs.pm/elixir/Map.html#merge/2) to combine `%{one: 1}` and `%{two: 2}`. @@ -820,7 +825,7 @@ Use [Map.merge/2](https://hexdocs.pm/elixir/Map.html#merge/2) to combine `%{one: ```elixir - +Map.merge(%{one: 1}, %{two: 2}) ``` Use [Map.update/4](https://hexdocs.pm/elixir/Map.html#update/4) or [Map.update!/3](https://hexdocs.pm/elixir/Map.html#update!/3) to update the `:count` key in this map to be `5` plus the existing value. @@ -836,7 +841,7 @@ Use [Map.update/4](https://hexdocs.pm/elixir/Map.html#update/4) or [Map.update!/ ```elixir -%{count: 10} +Map.update(%{count: 10}, :count, 10, &(&1 + 5)) ``` Use [Map.values/1](https://hexdocs.pm/elixir/Map.html#values/1) to retrieve the values `[1, 2, 3]` in the following map. @@ -852,7 +857,7 @@ Use [Map.values/1](https://hexdocs.pm/elixir/Map.html#values/1) to retrieve the ```elixir -%{key1: 1, key2: 2, key3: 3} +Map.values(%{key1: 1, key2: 2, key3: 3}) ``` ## The Keyword Module @@ -915,7 +920,7 @@ Use [Keyword.get/3](https://hexdocs.pm/elixir/Keyword.html#get/3) to access the ```elixir -[color: "red"] +Keyword.get([color: "red"], :color) ``` Use [Keyword.get/3](https://hexdocs.pm/elixir/Keyword.html#get/3) to access the value for the `:color` key in the following empty list. If the `:color` key does not exist, provide a default value of `"blue"`. @@ -931,7 +936,7 @@ Use [Keyword.get/3](https://hexdocs.pm/elixir/Keyword.html#get/3) to access the ```elixir -[] +Keyword.get([], :color, "blue") ``` Use the [Keyword.keys/1](https://hexdocs.pm/elixir/Keyword.html#keys/1) function to list all of the keys in the following keyword list. @@ -947,7 +952,7 @@ Use the [Keyword.keys/1](https://hexdocs.pm/elixir/Keyword.html#keys/1) function ```elixir -[one: 1, two: 2, three: 3] +Keyword.keys(one: 1, two: 2, three: 3) ``` Use the [Keyword.keyword?/1](https://hexdocs.pm/elixir/Keyword.html#keyword?/1) function to determine if the following is a keyword list. @@ -963,7 +968,7 @@ Use the [Keyword.keyword?/1](https://hexdocs.pm/elixir/Keyword.html#keyword?/1) ```elixir -[key: "value"] +Keyword.keyword?(key: "value") ``` Use the [Keyword.keyword?/1](https://hexdocs.pm/elixir/Keyword.html#keyword?/1) function to determine if an empty list is a keyword list. @@ -981,7 +986,7 @@ Use the [Keyword.keyword?/1](https://hexdocs.pm/elixir/Keyword.html#keyword?/1) ```elixir -[] +Keyword.keyword?([]) ``` Use the [Keyword.keyword?/1](https://hexdocs.pm/elixir/Keyword.html#keyword?/1) function to determine if the following list is a keyword list. @@ -997,7 +1002,7 @@ Use the [Keyword.keyword?/1](https://hexdocs.pm/elixir/Keyword.html#keyword?/1) ```elixir -[1, 2, 3] +Keyword.keyword?([1, 2, 3]) ``` ## Further Reading diff --git a/reading/comprehensions.livemd b/reading/comprehensions.livemd index 9d2eae025..4dffebbc8 100644 --- a/reading/comprehensions.livemd +++ b/reading/comprehensions.livemd @@ -222,7 +222,9 @@ each generator creates another nested loop and therefore has a significant perfo In the Elixir cell below, use a comprehension with a generator of `1..50` to create a list of even integers from `2` to `100`. ```elixir - +for n <- 1..50, rem(n, 2) == 0 do + n +end ``` ## Filters @@ -315,7 +317,9 @@ end ```elixir - +for a <- 1..7, b <- 1..7, c <- 1..7, a + b + c == 7 do + {a, b, c} +end ``` ## Collectables @@ -418,7 +422,9 @@ Enum.map(1..5, fn each -> each * 2 end) ``` ```elixir - +for n <- 1..5, n do + n * 2 +end ``` In the Elixir cell below, convert the following `Enum.reduce` into a comprehension. diff --git a/reading/enum.livemd b/reading/enum.livemd index 64a5398d2..f8859418c 100644 --- a/reading/enum.livemd +++ b/reading/enum.livemd @@ -371,7 +371,7 @@ Use [Enum.map/2](https://hexdocs.pm/elixir/1.12/Enum.html#map/2) to convert a li Enter your solution below. ```elixir - +Enum.map(1..10, fn int -> to_string(int) end) ``` ## Enum.reduce/2 and Enum.reduce/3 @@ -492,7 +492,13 @@ Use [Enum.reduce/3](https://hexdocs.pm/elixir/Enum.html#reduce/3) or [Enum.reduc Enter your solution below. ```elixir - +Enum.reduce(1..10, 0, fn int, acc -> + if rem(int, 2) == 0 do + acc + int + else + acc + end +end) ``` ## Enum.filter @@ -530,7 +536,8 @@ odd_numbers = Enum.filter(1..10, fn integer -> rem(integer, 2) != 0 end) Enter your solution below. ```elixir - +even = Enum.filter(1..10, fn int -> rem(int, 2) == 0 end) +odd = Enum.filter(1..10, fn int -> rem(int, 2) != 0 end) ``` ## Enum.all?/2 @@ -585,6 +592,7 @@ Use [Enum.all/2](https://hexdocs.pm/elixir/Enum.html#all/2) to determine if all ```elixir colors = [:green, :green, :red] +Enum.all?(colors, fn color -> color == :green end) ``` ## Enum.any/2 @@ -628,7 +636,8 @@ Enum.any?(1..10_000_000, fn integer -> is_bitstring(integer) end) Use [Enum.any/2](https://hexdocs.pm/elixir/Enum.html#any/2) to determine if any of the animals in the `animals` list are `:dogs`. You may change the `animals` list to experiment with [Enum.any/2](https://hexdocs.pm/elixir/Enum.html#any/2). ```elixir -[:cats, :dogs, :bears, :lions, :penguins] +animals = [:cats, :dogs, :bears, :lions, :penguins] +Enum.any?(animals, fn animal -> animal == :dogs end) ``` ## Enum.count/1 @@ -656,6 +665,8 @@ In the Elixir cell below, count the number of elements in the `collection`. It s ```elixir collection = [1, 2, 3, 4, 5] + +Enum.count(collection) ``` ## Enum.find/3 @@ -683,7 +694,9 @@ Enum.find(["h", "e", "l", "l", "o"], 10, fn each -> is_integer(each) end) Use [Enum.find/2](https://hexdocs.pm/elixir/Enum.html#find/2) to find the first even integer in this list. ```elixir -[1, "2", "three", 4, "five", 6] +mylist = [1, "2", "three", 4, "five", 6] + +Enum.find(mylist, fn item -> is_integer(item) && rem(item, 2) == 0 end) ``` ## Enum.random/1 @@ -710,7 +723,7 @@ Enum.map(1..10, fn _ -> Enum.random(0..9) end) Enter your solution below. ```elixir - +Enum.random(0..9) ``` ## Capture Operator and Module Functions diff --git a/reading/file.livemd b/reading/file.livemd index 6b33b16f0..d19278092 100644 --- a/reading/file.livemd +++ b/reading/file.livemd @@ -185,7 +185,8 @@ Path.join(path1, path2) Experiment with the functions above. Refer to the documentation for examples you can try. ```elixir - +path = __DIR__ +Path.split(path) ``` ## Bang Functions diff --git a/reading/keyword_lists.livemd b/reading/keyword_lists.livemd index b0d06161b..b8a3d435a 100644 --- a/reading/keyword_lists.livemd +++ b/reading/keyword_lists.livemd @@ -165,7 +165,7 @@ In the Elixir cell below, create a keyword list of your favourite super hero. In Enter your solution below. ```elixir - +[hero1: "superman", hero2: "batman", hero3: "robin"] ``` ## Accessing A Key @@ -184,6 +184,7 @@ Access the `:hello` key in the following keyword list. ```elixir keyword_list = [hello: "world"] +keyword_list[:hello] ``` ## Keyword List Operators @@ -206,13 +207,13 @@ evaluate as a tuple again. Remember that keyword lists are simply lists of tuple In the Elixir cell below, use `++` to add `[one: 1]` to `[two: 2]` to make `[one: 1, two: 2]`. ```elixir - +[one: 1] ++ [two: 2] ``` Remove `[two: 2]` from `[one: 1, two: 2]` to make `[one: 1]`. ```elixir - +[one: 1, two: 2] -- [two: 2] ``` ## Pattern Matching @@ -274,7 +275,8 @@ Bind `1` in the following keyword list to a variable `one`. Enter your solution below. ```elixir -[one: 1, two: 2, three: 3, four: 4] +[{key, one} | tail] = [one: 1, two: 2, three: 3, four: 4] +one ``` ## Further Reading diff --git a/reading/maps.livemd b/reading/maps.livemd index 3fc6d2a1c..f75b8f24a 100644 --- a/reading/maps.livemd +++ b/reading/maps.livemd @@ -193,6 +193,8 @@ map[:hello] ```elixir map = %{hello: "world"} +map.hello +map[:hello] ``` ### Updating Maps @@ -253,7 +255,9 @@ todo = %{title: "finish maps exercise", completed: false} ```elixir - +todo = %{title: "finish maps exercises", completed: false} +updated = %{todo | completed: true} +updated.completed ``` ## Pattern Matching @@ -332,7 +336,8 @@ Bind `2` and `4` in the following map to variables `two` and `four`. Enter your solution below. ```elixir -%{one: 1, two: 2, three: 3, four: 4} +%{two: two, four: four} = %{one: 1, two: 2, three: 3, four: 4} +four ``` ## Further Reading diff --git a/reading/modules.livemd b/reading/modules.livemd index 13b965088..1bf6dbdc6 100644 --- a/reading/modules.livemd +++ b/reading/modules.livemd @@ -308,7 +308,13 @@ end Enter your solution below. ```elixir +defmodule Languages.German do + def greeting do + "Guten Tag" + end +end +Languages.German.greeting() ``` ### Nested Modules @@ -347,8 +353,8 @@ We use the `@` symbol to define a compile-time module attribute. ```elixir defmodule Hero do - @name "Spider-Man" - @nemesis "Green Goblin" + @name "Batman" + @nemesis "Penguin" def introduce do "Hello, my name is #{@name}!" @@ -506,7 +512,29 @@ end ```elixir +defmodule Math do + @moduledoc """ + Math Module + Module demonstrates 2 functions with same name and different arity + """ + + @doc """ + add/2 + """ + def add(a, b) do + a + b + end + + @doc """ + add/3 + """ + def add(a, b, c) do + a + b + c + end +end +Math.add(2, 3) +Math.add(2, 3, 4) ``` ## Default Arguments @@ -573,7 +601,14 @@ Greeting.hello("Andrew") ``` ```elixir +defmodule Greeting do + def hello(name \\ "fubar") do + "Hello #{name}" + end +end +Greeting.hello("extra fubar") +Greeting.hello() ``` ## Documentation @@ -658,7 +693,7 @@ defmodule DoctestFailure do "expected return value" """ def test do - "actual return value" + "expected return value" end end ``` @@ -686,25 +721,27 @@ If this is the case, you either have to fix your code, or remove/comment the cra Uncomment the following code and evaluate the cell to see an example. Re-comment your code to avoid crashing the livebook as you continue with the lesson. ```elixir -# defmodule DoctestCrash do -# @moduledoc """ -# DoctestCrash +defmodule DoctestCrash do + @moduledoc """ + DoctestCrash -# Explains doctest crashes with examples -# """ + Explains doctest crashes with examples + """ -# @doc """ -# Purposely crashes doctest using invalid pattern matching + @doc """ + Purposely crashes doctest using invalid pattern matching -# ## Examples + ## Examples -# iex> {} = DoctestCrash.crash() -# "2" -# """ -# def crash do + iex> DoctestCrash.crash() + "2" + """ + def crash do + "2" + end +end -# end -# end +DoctestCrash.crash() ``` ### Your Turn diff --git a/reading/non_enumerables.livemd b/reading/non_enumerables.livemd index 724734dd2..cabb378ca 100644 --- a/reading/non_enumerables.livemd +++ b/reading/non_enumerables.livemd @@ -57,7 +57,7 @@ Integer.digits(123) In the Elixir cell below, convert the integer `4389` into a list of digits. ```elixir - +Integer.digits(4389) ``` ### Undigits @@ -138,7 +138,7 @@ Now your string is an enumerable list of characters. In the Elixir cell below, convert the string `"Hello, world!"` into a list of single characters. ```elixir - +String.split("Hello, world!", "") ``` ### Joining Strings @@ -216,6 +216,7 @@ defmodule CharacterCount do 4 """ def count(string) do + Enum.count(String.split(string, "", trim: true)) end end ``` diff --git a/reading/processes.livemd b/reading/processes.livemd index 22893ec05..0d8605fac 100644 --- a/reading/processes.livemd +++ b/reading/processes.livemd @@ -258,7 +258,14 @@ In the Elixir cell below, spawn a new process and send it a message `{:hello, "w `IO.puts` the message's payload where `"world"` is the payload. ```elixir +pid2 = + spawn(fn -> + receive do + {:hello, message} -> IO.puts(message) + end + end) +send(pid2, "world") ``` ## State @@ -363,6 +370,7 @@ defmodule Counter do receive do :increment -> loop(state + 1) + :decrement -> loop(state - 1) end end end @@ -373,7 +381,7 @@ counter_process = spawn(fn -> Counter.loop() end) You should be able to send a `:decrement` message to a spawned `Counter`. Uncomment and evaluate the code below to test your solution. ```elixir -# send(counter_process, :decrement) +send(counter_process, :decrement) ``` ## Mark As Completed diff --git a/reading/protocols.livemd b/reading/protocols.livemd index 03f2e357d..c11de4c48 100644 --- a/reading/protocols.livemd +++ b/reading/protocols.livemd @@ -193,7 +193,13 @@ end ```elixir +defimpl Adder, for: List do + def add(list1, list2) do + list1 ++ list2 + end +end +Adder.add([1], [2]) ``` ## Protocols With Structs @@ -245,8 +251,21 @@ Sound.say(%Cat{mood: :angry}) ```elixir defmodule Dog do - defstruct [] + defstruct [:mood] +end +``` + +```elixir +defimpl Sound, for: Dog do + def say(dog) do + case dog.mood do + :happy -> "Bark!" + :angry -> "Growl!" + end + end end + +Sound.say(%Dog{mood: :happy}) ``` Define a `Sound` implementation for the `Dog` struct above. diff --git a/reading/ranges.livemd b/reading/ranges.livemd index 342c427b9..e089b5708 100644 --- a/reading/ranges.livemd +++ b/reading/ranges.livemd @@ -121,7 +121,7 @@ In the Elixir cell below, use [Enum.to_list/1](https://hexdocs.pm/elixir/Enum.ht ```elixir - +Enum.to_list(3..9//3) ``` ### Pattern Matching With Ranges diff --git a/reading/start_here.livemd b/reading/start_here.livemd index 6e4bfd8ae..91ef12af0 100644 --- a/reading/start_here.livemd +++ b/reading/start_here.livemd @@ -99,6 +99,10 @@ There is a **Commit Your Progress** and a **Mark as Completed** section at the e However, self-led students can complete these sections if they want to track their progress and get more practice using GitHub. We ask that you don't create pull requests to the DockYard Academy repo, as this spams our PR tracker. If you are not an academy student, we also ask that you refrain from @mentioning the instructor as this spams our notifications, and 1-1 feedback is only available to academy students. +```elixir +2 + 2 +``` + ## Mark As Completed diff --git a/reading/strings_and_binaries.livemd b/reading/strings_and_binaries.livemd index d1be8eff8..b5a4ce294 100644 --- a/reading/strings_and_binaries.livemd +++ b/reading/strings_and_binaries.livemd @@ -85,6 +85,10 @@ Each byte stores an integer between `1` and `255` in binary. These integers are ?A ``` +```elixir +?Z +``` + We can also see that a string is actually a series of binary bytes representing a codepoint using [IO.inspect/2](https://hexdocs.pm/elixir/IO.html#inspect/2) with the `binaries: :as_binaries` option. ```elixir @@ -218,13 +222,13 @@ String.graphemes("eĢ") Use [String.graphemes/1](https://hexdocs.pm/elixir/String.html#graphemes/1) to convert the [woman fire fighter](https://emojipedia.org/woman-firefighter/) emoji šŸ‘©ā€šŸš’ to a list of graphemes. ```elixir - +String.graphemes("šŸ‘©ā€šŸš’") ``` Use [String.codepoints/1](https://hexdocs.pm/elixir/String.html#codepoints/1) to convert the emoji šŸ‘©ā€šŸš’ into a list of codepoints. You'll notice it's actually a combination of the šŸ‘© and šŸš’ emojis. ```elixir - +String.codepoints("šŸ‘©ā€šŸš’") ``` ## Hexadecimal @@ -316,7 +320,7 @@ Use hexadecimal `0x` syntax to represent the number `15`. ```elixir - +0xF ``` ## Bitstrings @@ -416,7 +420,7 @@ Convert the alphabet `"abcdefghijklmnopqrstuvwxyz"` to a charlist, then inspect ```elixir - +IO.inspect(String.to_charlist("abcdefghijklmnopqrstuvwxyz"), charlists: :as_lists) ``` ## Further Reading diff --git a/reading/task.livemd b/reading/task.livemd index 70412b63f..4e7553800 100644 --- a/reading/task.livemd +++ b/reading/task.livemd @@ -242,7 +242,8 @@ In the Elixir cell below, spawn a task which takes one second to complete. `await/2` the task and alter the timeout value to be one second. Awaiting the task should crash. ```elixir - +task = Task.async(fn -> Process.sleep(1000) end) +Task.await(task, 1000) ``` ## Further Reading diff --git a/reading/typespecs.livemd b/reading/typespecs.livemd index 0b435ac7b..8ee71e39b 100644 --- a/reading/typespecs.livemd +++ b/reading/typespecs.livemd @@ -66,6 +66,7 @@ In the `Adder` module below, create an `@spec` for integers with the `add/2` fun ```elixir defmodule Adder do + @spec add(integer(), integer()) :: integer() def add(integer1, integer2) do integer1 + integer2 end @@ -108,6 +109,7 @@ In the `Math` module below, define a custom `@type` `input()` that can be a list ```elixir defmodule Math do + @type mynumber :: list() | integer() | charlist() end ```