Понимание функций Elixir с несколькими предложениями - PullRequest
0 голосов
/ 09 декабря 2018

Я недавно начал изучать эликсир.Исходя из объектно-ориентированного программирования, у меня возникают проблемы с пониманием функций Elixir.

Я следую книге Дэйва Томаса Программирование Elixir > = 1.6, но я не совсем понимаю, как работают функции.

В книге у него есть следующий пример:

handle_open = fn
  {:ok, file} -> "Read data: #{IO.read(file, :line)}"
  {_,  error} -> "Error: #{:file.format_error(error)}"
end

handle_open.(File.open(​"​​code/intro/hello.exs"​))   ​# this file exists​
-> "Read data: IO.puts \"Hello, World!\"\n"

 handle_open.(File.open(​"​​nonexistent"​))           ​# this one doesn't​
 -> Error: no such file or directory"

Я не понимаю, как работают параметры.Есть ли скрытое утверждение if, else где-то скрытое?

Ответы [ 3 ]

0 голосов
/ 09 декабря 2018

В elixir вы можете определять функции с несколькими предложениями, а elixir использует так называемое сопоставление с шаблоном , чтобы определить, какое предложение выполнить.Как вы и подозревали, когда вы определяете несколько предложений функций, вы фактически создаете неявные if-else if, например, , если аргументы функции, указанные в вызове функции, соответствуют параметрам первого предложения функции, then выполнить первое предложение функции, иначе, если аргументы функции, указанные в вызове функции, соответствуют параметрам второго предложения функции, затем выполнить второе предложение функции и т. Д. .Вот пример:

my.exs:

defmodule My do

  def calc(x, 1) do
    x * 3
  end
  def calc(x, 2) do
    x - 4
  end

  def test do
    IO.puts calc(5, 1)
    IO.puts calc(5, 2)
  end

end

(Я обычно не оставляю пустую строку между несколькими предложениями определения функции, чтобы указать, что они все являются одной и той же функцией.)

В iex:

$ iex my.exs
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)

iex(1)> My.test
15
1
:ok

iex(2)>

При вызове calc(5, 1) эликсир переходит к определению calc(), а эликсир начинается с первого предложения функции и пытается сопоставить аргументы5, 1 против параметров x, 1.Поскольку параметр x является переменной, он может соответствовать чему угодно, тогда как параметр 1 может соответствовать только 1.Следовательно, есть совпадение, и x получает значение 5.В других языках у вас не может быть значений, таких как 1, "hello", %{a: 1} или :error, указанных в качестве параметра функции в определении функции - скорее все параметры функции должны быть переменными.

Аналогично, когда вы вызываете calc(5, 2), эликсир переходит к определению calc(), и эликсир начинается с первого предложения функции и пытается сопоставить аргументы 5, 2 в вызове функции с параметрами x, 1.Параметр x может соответствовать чему угодно, но параметр 1 не соответствует аргументу 2, поэтому elixir переходит к следующему условию функции и пытается сопоставить аргументы 5, 2 с параметрами x, 2.Это приводит к совпадению, и x получает назначение 5.

В Elixir есть довольно интересные правила сопоставления.Вот пара правил, с которыми вы можете столкнуться в какой-то момент, которые может быть трудно расшифровать:

  1. def func(%{] = x) будет соответствовать любому аргументу, который является картой - не простопустая карта - и карте будет присвоена переменная x.То же самое касается структур, например, def func(%Dog{} = x) будет соответствовать любой структуре Dog.

  2. def func("hello " <> subject) будет соответствовать любому строковому аргументу, который начинается с "hello ", а остальная часть строки будетприсваивается переменной subject.

Elixir также позволяет определять анонимные функции с несколькими предложениями .Если вы измените test() на:

  def test do
    func = fn 
              (:ok, x)    -> IO.puts ":ok branch, x = #{x}"
              (y, :error) -> IO.puts ":error branch, y = #{y}"
           end

    func.("hello", :error)
    func.(:ok, 10)
  end

, то в iex вы увидите:

~/elixir_programs$ iex my.exs
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)

iex(1)> My.test()  
:error branch, y = hello
:ok branch, x = 10
:ok

iex(2)> 

Соответствие работает так же, как и с именованными функциями: elixir просматривает предложения функций по порядкуи пытается сопоставить аргументы в вызове функции с параметрами в предложениях функции.Обратите внимание, что порядок, в котором вы определяете функциональные предложения в вашем коде, может иметь значение.Легко определить функциональное предложение, которое никогда не будет выполнено, например:

func = fn 
          (x)    ->  ...
          (:ok)  ->  ...
       end

Поскольку параметр x в первом предложении функции будет соответствовать любому аргументу, второе предложение функции никогда не сможет выполняться.К счастью, elixir предупредит вас, если вы сделаете это.

И, поскольку elixir - это функциональный язык, было бы упущением не показывать пример рекурсии.Добавьте следующее определение count() в модуль My:

  def count(0) do
    :ok
  end
  def count(n) when n > 0 do
    Process.sleep 1_000  #sleep for 1 second
    IO.puts n
    count(n-1)
  end

Затем в iex:

 ~/elixir_programs$ iex my.exs 
 Erlang/OTP 20 [erts-9.3] [source]
 [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe]
 [kernel-poll:false] Interactive Elixir (1.6.6) - press Ctrl+C to exit
 (type h() ENTER for help)

iex(1)> My.count(10)
10
9
8
7
6
5
4
3
2
1
:ok

iex(2)>

Когда код вызывает count(n-1), elixir переходит к первому условию функции count(), затем пытается сопоставить любое значение аргумента n-1 с параметром 0 в предложении функции.Если совпадений нет, elixir пробует второе предложение функции.В результате второе предложение функции продолжает соответствовать вызову функции count(n-1) до тех пор, пока все числа, включая 1, не будут выведены, после чего n-1 будет равно 0 в вызове функции, что приведет к вызову функции count(0), что в конечном итоге будет соответствоватьпервое функциональное предложение и первое функциональное предложение ничего не выводят и не вызывают сами, поэтому рекурсия заканчивается.

Ключевые вещи, которые нужно изучить в elixir:

  1. Сопоставление с образцом
  2. Рекурсия
  3. Создание других процессов

Удачи!

0 голосов
/ 17 декабря 2018

Хорошим объяснением для работы с файлами является эта книга: joyElixir .Это хорошее место для начала и небольшая книга.

0 голосов
/ 09 декабря 2018

Здесь происходит несколько вещей, и я постараюсь охватить все из них.Для начала, здесь используются две разные функции.Одна из них - именованная функция (File.open), а другая - созданная вами анонимная функция, назначенная переменной handle_open.Существует небольшое различие в том, как оба они называются .

Когда вы вызываете функцию File.open внутри функции handle_open, это в основном означает, что вы вызываете handle_open для ее результата.,Но сама функция File.open/2 может возвращать два значения:

  1. {:ok, file}, если файл существует
  2. {:error, reason}, если его нет (илиесли есть другая ошибка)

Функция handle_open использует сопоставление с шаблоном и несколько функциональных предложений , чтобы проверить, что был ответ, и возвращает соответствующее сообщение.Если данное значение «соответствует указанному шаблону», он выполняет этот оператор, в противном случае он проверяет следующий шаблон.Хотя в некотором смысле это похоже на оператор if-else, более подходящая аналогия - ключевое слово case:

result = File.open("/some/path")

case result do
  {:ok, file} ->
    "The file exists"

  {:error, reason} ->
    "There was an error"
end
...