Fork me on GitHub

Kontrollstrukturer

I denne leksjonen skal vi ta en nærmere titt på de forskjellige kontrollstrukturene Elixir har tilgjengelig.

Innholdsfortegnelse

if og unless

Sannsynligheten er stor for at du har vært borti if/2 tidligere, og har du tidligere programmert i Ruby kjenner du til unless/2. De virker på samme måte i Elixir, men er her definert som makroer, og ikke språk konstruksjoner. Du kan finne implementeringen i Kernel modulen.

Det er verdt å merke seg at kun verdien nil og den boolske verdien false er “usant” i Elixir.

iex> if String.valid?("Hello") do
...>   "Valid string!"
...> else
...>   "Invalid string."
...> end
"Valid string!"

iex> if "a string value" do
...>   "Truthy"
...> end
"Truthy"

unless/2 er motsatt av if/2 - Ellers fungerer den på samme måte:

iex> unless is_integer("hello") do
...>   "Not an Int"
...> end
"Not an Int"

case

Om vi trenger å sammenligne mot forskjellige mønster, kan vi bruke case:

iex> case {:ok, "Hello World"} do
...>   {:ok, result} -> result
...>   {:error} -> "Uh oh!"
...>   _ -> "Catch all"
...> end
"Hello World"

Variabelen _ er en viktig del av case. Uten den vil funksjonen gi oss ei feilmelding, hvis det ikke finnes en match.

iex> case :even do
...>   :odd -> "Odd"
...> end
** (CaseClauseError) no case clause matching: :even

iex> case :even do
...>   :odd -> "Odd"
...>   _ -> "Not Odd"
...> end
"Not Odd"

Variabelen _ blir som else og vil matche “alt annet”.

Siden case avhenger av mønstersammenligning, gjelder de samme reglene og restriksjonene. Hvis du ønsker å sammenligne med en eksisterende variabel, må festeoperatoren ^ benyttes:

iex> pie = 3.14 
3.14
iex> case "cherry pie" do
...>   ^pie -> "Not so tasty"
...>   pie -> "I bet #{pie} is tasty"
...> end
"I bet cherry pie is tasty"

En annen fiffig funksjon i case, er dens støtte for beskyttelsesklausuler (guard clauses):

Dette eksemplet er hentet direkte fra den offisielle Elixir Getting Started guiden.

iex> case {1, 2, 3} do
...>   {1, x, 3} when x > 0 ->
...>     "Will match"
...>   _ ->
...>     "Won't match"
...> end
"Will match"

Se den offisielle dokumentasjonen for Tillatte uttrykk i beskyttelsesklausuler.

cond

Hvis vi trenger å sammenligne forhold, og ikke verdier, kan vi bruke cond.
Denne funksjonen kan sammenlignes med else if eller elsif i andre programmeringsspråk:

Dette eksemplet er hentet direkte fra den offisielle Elixir Getting Started guiden.

iex> cond do
...>   2 + 2 == 5 ->
...>     "This will not be true"
...>   2 * 2 == 3 ->
...>     "Nor this"
...>   1 + 1 == 2 ->
...>     "But this will"
...> end
"But this will"

På samme måte som case, vil cond gi oss ei feilmelding hvis det ikke er noen match. Vi kan håndtere dette ved å definere en betingelse til true:

iex> cond do
...>   7 + 1 == 0 -> "Incorrect"
...>   true -> "Catch all"
...> end
"Catch all"

with

with er nyttig hvis vi for eksempel benytter oss av et nestet case statement eller forhold som ikke enkelt kan transporteres sammen. with består av nøkkelord, generatorer og et utrykk.

Vi vil diskutere generatorer nærmere i en senere leksjon, men alt vi trenger å vite nå er at de benytter mønstersammenligning til å sammenligne den høyre siden <- med den venstre.

Vi starter med et enkelt eksempel av with:

iex> user = %{first: "Sean", last: "Callan"}
iex> with {:ok, first} <- Map.fetch(user, :first),
...>      {:ok, last} <- Map.fetch(user, :last),
...>      do: last <> ", " <> first
"Callan, Sean"

Hvis utsagnet ikke kan sammenlignes, vil den ikke-sammenlignbare verdien returneres:

iex> user = %{first: "doomspork"}
%{first: "doomspork"}
iex> with {:ok, first} <- Map.fetch(user, :first),
...>      {:ok, last} <- Map.fetch(user, :last),
...>      do: last <> ", " <> first
:error

La oss nå se på et større eksempel uten with, og deretter se hvordan vi kan refaktorere det:

case Repo.insert(changeset) do
  {:ok, user} ->
    case Guardian.encode_and_sign(resource, :token, claims) do
      {:ok, jwt, full_claims} ->
        important_stuff(jwt, full_claims)
      error -> error
    end
  error -> error
end

Når vi introduserer with til eksemplet, ender vi opp med kode som er enklere å lese, og som består av færre linjer:

with
  {:ok, user} <- Repo.insert(changeset),
  {:ok, jwt, full_claims} <- Guardian.encode_and_sign(user, :token),
  do: important_stuff(jwt, full_claims)

Del denne siden