Синтаксис эликсира - определение нескольких функций с одинаковыми сигнатурами и без конечного ключевого слова - PullRequest
0 голосов
/ 25 октября 2018

Я проверяю пример приложения: https://github.com/shamshirz/scoreboard/blob/master/lib/scoreboard/games/games.ex

Я наткнулся на следующий код, который я разметил на основе моего понимания.

def query(Score, params) do #defined function query/2
    params
    |> Map.put_new(:limit, 10) #adding to the "dictionary"
    |> Map.put_new(:order, :total) #""
    |> Map.to_list() #flattening out the map
    |> Enum.reduce(Score, &apply_param/2) #call apply_param/2 for each item
  end

#defines query/2... now we have two query/2... Wait, how is this?
#I guess this one is query/2 with a private argument (_params). 
#Also, this doesnt have an `end`
  def query(queryable, _params), do: queryable 

#defines apply_param/2 with no end
  def apply_param({:limit, num}, queryable), do: queryable |> limit(^num) 

#defines another apply_param/2, no end again!
  def apply_param({:order, field}, queryable), do: queryable |> order_by(desc: ^field)

#defines another apply_param/2, no end again!...
  def apply_param({:player_id, player_id}, queryable),
    do: queryable |> where(player_id: ^player_id)

#again...
  def apply_param(_param, queryable), do: queryable

#finally a function I can read (I think)
#take in a query and execute, if its null -> error, result -> return with :ok
  def get(queryable, id) do
    case Repo.get(queryable, id) do
      nil ->
        {:error, :not_found}

      result ->
        {:ok, result}
    end
  end
  #look at those nice "end"-ings

Что это за определения apply_param?Почему у них нет end и как они все могут иметь схожие подписи?То есть они принимают tuple и вторую queryable переменную?

Ответы [ 2 ]

0 голосов
/ 25 октября 2018

Вам не нужно ключевое слово end, если вы используете однострочную функцию.Для создания однострочной функции вы используете do с предыдущей командой и завершающим двоеточием.Следующие функции одинаковы:

# Single line
def say_hello(name), do: IO.puts("Hello #{name}")

# Multiline
def say_hello(name) do
  IO.puts("Hello #{name}")
end

Для вашего другого вопроса все определения apply_param/2 являются примером сопоставления с образцом.Каждая функция сопоставляется с другим параметром, который применяется в операторе reduce в первой функции.

Список параметров может выглядеть следующим образом:

[{:limit, 5}, {:order, :asc}]

Так чтоВызов Reducer будет вызываться примерно так:

Enum.reduce([{:limit, 5}, {:order, :asc}], score,  &apply_param/2)

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

{:limit, 5} параметр будет соответствовать первой функции, в то время как {:order, :asc} не будет соответствовать первой функции, но будет соответствовать второй функции.

def apply_param({:limit, num}, queryable), do: queryable |> limit(^num) 

def apply_param({:order, field}, queryable), do: queryable |> order_by(desc: ^field)

def apply_param({:player_id, player_id}, queryable),
    do: queryable |> where(player_id: ^player_id)

def apply_param(_param, queryable), do: queryable

В последней функции нижнее подчеркивание в _param означаетчто это будет соответствовать чему угодно.Как правило, это хорошая идея: иметь регистр по умолчанию для неизвестного ввода.


Вот пример того, что я часто делаю, который демонстрирует обе эти вещи:

# This function declaration without a body is called a header, it is used so # I don't have define the default value of 5 in multiple places
def fetch_data(id, retries_left \\ 5)

# In this case, where the retries_left matches on the value of 0, I want to
# raise an error.  Since this is small, I use a one-line function
def fetch_data(id, 0), do: raise "Retries exhausted, could not fetch data!"

# In any other case, try getting the data again
def fetch_data(id, retries_left) do
  case do_request(id)
    # It didn't work, decrement and try again, this will eventually get us to 0
    {:error, _} -> fetch_data(id, retries_left - 1)

    # It works!  Return the payload
    {:ok, payload} -> payload
  end
end
0 голосов
/ 25 октября 2018

Однострочные функции

Это короткая рука для написания однострочных функций эликсира.Например, вы можете написать эту функцию:

def add(x, y) do
  x + y
end

Например:

def add(x, y), do: x + y

Обратите внимание на do: вместо do.


Сопоставление с образцом

Другая вещь, которую использует код, - это Сопоставление с образцом в функциональных предложениях.Это позволяет разработчикам избегать множества условных операторов для принятия решений, вместо этого позвольте компилятору выбирать, какое определение функции вызывать.

Например, посмотрите это:

def something(num, :inc), do: num + 1
def something(num, :dec), do: num - 1

После определения эти дваэкземпляры функции something/2 будут обрабатывать аргументы по-разному.Поэтому, если вы вызовете something(4, :inc), он «совпадет» с первым определением и вернет 5, если вы передадите :dec, вместо этого он вернет 3, но если второй аргумент будет чем-то другим, он выдаст UndefinedFunctionClauseError.

...