From df82fb924268de9808e697e104ed1515570478fa Mon Sep 17 00:00:00 2001 From: SowinskiBraeden Date: Tue, 8 Sep 2026 11:34:36 -0700 Subject: [PATCH] lecture 1 --- lectures/01/lec1.ex | 84 ++++++++++++++++++++++++++++++ lectures/01/notes.exs | 118 ++++++++++++++++++++++++++++++++++++++++++ note.md | 8 +++ 3 files changed, 210 insertions(+) create mode 100644 lectures/01/lec1.ex create mode 100644 lectures/01/notes.exs create mode 100644 note.md diff --git a/lectures/01/lec1.ex b/lectures/01/lec1.ex new file mode 100644 index 0000000..354afd3 --- /dev/null +++ b/lectures/01/lec1.ex @@ -0,0 +1,84 @@ +defmodule Lec1 do + def fact(n) do + if n <= 0 do + 1 + else + n * fact(n - 1) + end + end + + def fact2(0) do 1 end # multi-clause function + def fact2(n) do n * fact2(n - 1) end + + def fact3(n) when n <= 0 do 1 end # using guard + def fact3(n) do n * fact3(n - 1) end + + # What on earth is going on? + # do: with commas eliminates end? + def fact4(n) when n <= 0, do: 1 + def fact4(n), do: n * fact3(n - 1) + + # private functions to this module, i.e. no Lec1.fact5 on these + defp fact5(n, acc) when n <= 0, do: acc + defp fact5(n, acc), do: fact5(n - 1, n * acc) + + # we can call this function internally though + def fact5(n), do: fact5(n, 1) + + # ------- pattern matching ------- + + def dedup(l) do + case l do + [x, x | rest] -> dedup([x | rest]) + [x | rest] -> [x | dedup(rest)] + _ -> l + end + end + + # or matching within pattern matching + def dedup1(l) do + case l do + [x | rest = [x | _]] -> dedup(rest) + [x | rest] -> [x | dedup(rest)] + _ -> l + end + end + + # ------- + defp fizzbuzz_print(n) do + cond do + rem(n, 15) == 0 -> IO.puts("fizzbuzz") + rem(n, 5) == 0 -> IO.puts("buzz") + rem(n, 3) == 0 -> IO.puts("fizz") + true -> IO.puts(n) + end + end + + defp fizzbuzz(i, n) when i > n, do: :ok + defp fizzbuzz(i, n) do + fizzbuzz_print(i) + fizzbuzz(i + 1, n) + end + + def fizzbuzz(n), do: fizzbuzz(1, n) + + # -------- + def qsort([]), do: [] + def qsort([h | t]), do: qsort( Enum.filter(t, &(&1 < h)) ) ++ + [h] ++ qsort ( Enum.filter(t, &(&1 > h)) ) +end + +# why the above works +# imagine the following +if 1 < 2 do "OK" else "BAD" end + +# is similar to +if(1 < 2, do: "OK", else: "BAD") + +# consider a key-word list +[{:a, 1}, {:b, 2}] + +# so do: ok is esentially a key-word list +if(1 < 2, [do: "OK", else: "BAD"]) + +# somehow this works diff --git a/lectures/01/notes.exs b/lectures/01/notes.exs new file mode 100644 index 0000000..588d29e --- /dev/null +++ b/lectures/01/notes.exs @@ -0,0 +1,118 @@ +## Sep. 08, 2026 - Braeden Sowinski + +# h can be used to find info on a function +h is_boolean + +# function brackets are optional but recommended +is_boolean "hello" +is_boolean(false) + +3/2 # -> 1.5 results in a float +div(3,2) # -> 1 results in an int + +### Atoms +# - Create atoms by starting with a : +# ANYTHING that starts with an uppercase is an atom +## atoms are constants whos value is their own name +:hello_world +Hello # is an atom because it starts with uppercase + +# Similarly module names must start with uppercase, and are therefor atoms +# e.g. +Hello = :"Elixir.Hello" # -> true + +# nil is an atom but nil is built in +nil +:nil +nil == :nil + +# Short circuits returns second cond +1 < 2 and false # -> results in false +1 < 2 and 3 # -> results in 3 + +1 and false # -> errors out, first must be a boolean +1 && false # -> works + +# and or not operators + +# erlang modules start with lowercase and you can call them by using +# a colon before the module name, this makes it an atom which are modules +:math.pi + +# in erlang syntax its math:pi + +# variables in erlang start with uppercase where in elixir they start with lowercase +x = 1 + +# variables are immutable but can be rebound +# i.e. this is a different x +x = 2 +2 = x + +# '=' is not an assignment, its a match operator trying to match left to right + +{x, 2} = {1, 2} # matches, and x becomes 1 + +# you can pin the value of x with ^ +{^x, 2} = {3, 2} # this wont match since x is pinned to 1, and we are trying to match with 3 + +# if a match is successful, it will always return the right hand side of the match + +{a, "hello", b} = {2, "hello", :atom} +a # -> 2 +b # -> :atom + +# you can use x more than once as long as it matches +{x, x, 1} = {"hello", "world", 1} # -> no match +{x, x, 1} = {"hello", "hello", 1} # -> matches +x # -> "hello" + +### List type +[1, 2, "hello"] + +# cons operator is | similar to `||` in ocaml +[1 |[2, "hello"]] # -> same as above +[1 | [2 | ["hello"]]] # -> same as above + +# cons operator can be used to pattern match +[h | t] = [1, 2, 3] +h # -> 1 +t # -> [2, 3] + +[_, x, _, y | _] = [1, "hello", 2, "world", 5] +x # -> "hello" +y # -> "world" + +# `_` can be used to match and just throw that junk away, +# similarly we can do `| _` to match the remaining to nothing + +### Anonymous Functions +f = fn x -> x + 1 end + +# to call an anon function use .() +f.(1) + +String.upcase("hello") # -> "HELLO" + +# functions passed to other functions need to be anon functions +Enum.map(["hello", "world"], fn x -> String.upcase(x) end) + +# Another way to do this is to capture a function to make it anon +# we specify String.upcase/1 since there are two version of upcase +# one with /1 argument and another upcase with /2 arguments +Enum.map(["hello", "world"], &String.upcase/1) + +Enum.map([1, 2], fn x -> x * x end) +Enum.map([1, 2], &(&1 * &1)) # captured anon function, &1 is first argument + +# or + +Enum.map([1, 2], fn x -> x + 1 end) +Enum.map([1, 2], &(&1 + 1)) + +# LIST CONCAT / DIFF +[1, 2] ++ [3, 4] +[1, 2] -- [1] # -> [2] + +# STRING CONCAT +"hello" <> " " <> "world" diff --git a/note.md b/note.md new file mode 100644 index 0000000..ce8456e --- /dev/null +++ b/note.md @@ -0,0 +1,8 @@ +## Shells +`iex` invokes elixir shell +`erl` invokes erlang shell + +`.ex` is for compiled elixir code +`.exs` is for interpreted code + +`iex file.ex`