Do you want to pick up from where you left of?
Take me there

Δομές Ελέγχου

Σε αυτό το μάθημα θα δούμε τις δομές ελέγχου που μας παρέχει η Elixir.

Πίνακας περιεχομένων

if και unless

Λογικά έχετε συναντήσει την if/2 ξάνα, και αν έχετε χρησιμοποιήσει την Ruby είστε εξοικειομένοι με την unless/2. Στην Elixir δουλεύουν σχεδόν το ίδιο, αλλά είναι ορισμένες ως μακροεντολές, όχι σαν δομές της γλώσσας. Μπορείτε να βρείτε την υλοποίησή τους στην ενότητα Kernel.

Θα πρέπει να σημειωθεί ότι στην Elixir, οι μόνες τιμές που περνάνε ως false είναι η nil και η δυαδική false.

iex> if String.valid?("Hello") do
...>   "Έγκυρο αλφαριθμητικό."
...> else
...>   "Άκυρο Αλφαριθμητικό."
...> end
"Έγκυρο αλφαριθμητικό."

iex> if "μια αλφαριθμητική τιμή" do
...>   "Αληθής"
...> end
"Αληθής"

Η unless/2 είναι παρόμοια με την if/2, εκτός του ότι λειτουργεί όταν είναι αρνητικός ο έλεγχος:

iex> unless is_integer("γεια") do
...>   "Δεν είναι ακέραιος"
...> end
"Δεν είναι ακέραιος"

case

Αν είναι απαραίτητο να αντιπαραβάλουμε πολλαπλά πρότυπα μπορούμε να χρησιμοποιήσουμε την case/2:

iex> case {:ok, "Γειά σου κόσμε!"} do
...>   {:ok, result} -> result
...>   {:error} -> "Ωχ όχι!"
...>   _ -> "Όλα τα υπόλοιπα"
...> end
"Γειά σου κόσμε!"

Η μεταβλητή _ είναι μια σημαντική προσθήκη στις εντολές case/2. Χωρίς αυτήν, μια αποτυχία να βρεθεί αντιπαραβολή θα σηκώσει ένα σφάλμα:

iex> case :even do
...>   :odd -> "Μονός"
...> end
** (CaseClauseError) no case clause matching: :even

iex> case :even do
...>   :odd -> "Μονός"
...>   _ -> "Όχι Μονός"
...> end
"Όχι Μονός"

Μπορείτε να σκέφτεστε την μεταβλητή _ σαν το else το οποίο θα αντιπαραβάλει όλα τα υπόλοιπα.

Εφόσον η case βασίζεται στην αντιπαραβολή προτύπων, ισχύουν όλοι οι κανόνες και περιορισμοί. Αν σκοπεύετε να αντιπαραβάλετε υπάρχουσες μεταβλητές πρέπει να χρησιμοποιήσετε τον τελεστή καρφίτσας ^:

iex> pie = 3.14
 3.14
iex> case "μηλόπιτα" do
...>   ^pie -> "Όχι και τόσο νόστιμη"
...>   pie -> "Βάζω στοίχημα ότι η #{pie} είναι πεντανόστιμη"
...> end
"Βάζω στοίχημα ότι η μηλόπιτα είναι πεντανόστιμη"

Ακόμα ένα πολύ καλό χαρακτηριστικό της case/2 είναι η υποστήριξή της για ρήτρες προστασίας:

Αυτό το παράδειγμα προέρχεται κατευθείαν από τον επίσημο οδηγό της Elixir, Getting Started.

iex> case {1, 2, 3} do
...>   {1, x, 3} when x > 0 ->
...>     "Θα ταιριάξει"
...>   _ ->
...>     "Δεν θα ταιριάξει"
...> end
"Θα ταιριάξει"

Ελέγξτε τα επίσημα έγγραφα για τις Εκφράσεις που επιτρέπονται στις ρήτρες προστασίας.

cond

Όταν χρειάζεται να αντιπαραβάλουμε συνθήκες αντί για τιμές μπορούμε να στραφούμε στην cond/1. Αυτή είναι όμοια με τις else if και elsif από άλλες γλώσσες:

Αυτό το παράδειγμα προέρχεται κατευθείαν από τον επίσημο οδηγό της Elixir, Getting Started.

iex> cond do
...>   2 + 2 == 5 ->
...>     "Αυτό δεν θα είναι αληθές"
...>   2 * 2 == 3 ->
...>     "Ούτε αυτό"
...>   1 + 1 == 2 ->
...>     "Αλλά αυτό θα είναι"
...> end
"Αλλά αυτό θα είναι"

Όπως η case/2, η cond/1 θα σηκώσει ένα σφάλμα αν δεν υπάρχει αντιπαραβολή. Για να το ελέγξουμε αυτό, μπορούμε να ορίσουμε ένα σετ συνθηκών σε true:

iex> cond do
...>   7 + 1 == 0 -> "Αναληθές"
...>   true -> "Σύλληψη όλων"
...> end
"Σύλληψη όλων"

with

Η ειδική μορφή της with/1 είναι χρήσιμη όταν θέλετε να χρησιμοποιήσετε μια ένθετη εντολή case/2 ή σε περιπτώσεις που δεν μπορούν να σωληνωθούν μαζί. H έκφραση with/1 συντίθεται με την λέξη κλειδί, τις γεννήτριες, και τέλος μια έκφραση.

Θα συζητήσουμε τις γεννήτριες περισσότερο στο μάθημα Κατανόηση Λιστών αλλά για τώρα πρέπει μόνο να ξέρουμε ότι χρησιμοποιούν αντιπαραβολή προτύπων για να συγκρίνουν την δεξιά μεριά του <- με την αριστερή.

Θα ξεκινήσουμε με ένα απλό παράδειγμα της with/1 και τότε θα δούμε κάτι περισσότερο:

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

Στην περίπτωση που μια έκφραση αποτυγχάνει να αντιπαραβληθεί, η τιμή που δεν αντιπαραβάλεται επιστρέφεται:

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

Τώρα ας δούμε ένα μεγαλύτερο παράδειγμα χωρίς την with/1 και τότε θα δούμε πως θα την ανακατασκευάσουμε:

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

      error ->
        error
    end

  error ->
    error
end

Όταν εισάγουμε την with/1, καταλήγουμε με κώδικα που είναι έυκολο να καταλάβουμε και έχει λιγότερες γραμμές:

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

Από την Elixir 1.3 και μετά, οι εκφράσεις with/1 υποστηρίζουν την else:

import Integer

m = %{a: 1, c: 3}

a =
  with {:ok, number} <- Map.fetch(m, :a),
       true <- is_even(res) do
    IO.puts("#{number} διαιρούμενο με το 2 ισούται με #{div(number, 2)}")
    :even
  else
    :error ->
      IO.puts("Δεν έχουμε αυτό το στοιχείο στο χάρτη")
      :error

    _ ->
      IO.puts("Είναι μονός")
      :odd
  end

Βοηθάει στον έλεγχο σφαλμάτων με την παροχή αντιπαραβολής προτύπων τύπου case. Η τιμή που περνάει σε αυτήν είναι η πρώτη μη-αντιπαραβλημένη έκφραση.

Έπιασες λάθος ή θέλεις να συνεισφέρεις στο μάθημα; Επεξεργαστείτε αυτό το μάθημα στο GitHub!