Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/elixir/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Elixir 如何重构替换参数模式匹配的函数_Elixir - Fatal编程技术网

Elixir 如何重构替换参数模式匹配的函数

Elixir 如何重构替换参数模式匹配的函数,elixir,Elixir,我有这个功能: def update(%Evento{} = evento, attrs, dataSchema) do evento |> dataSchema.changeset(attrs) |> Repo.update() end 它与%Evento{}结构绑定。 我想让它独立于结构,而是传递一个参数,这样在调用函数时,我可以传递%Evento{},%News{},%Contact{}或任何我想要的结构,同时保持相同的功能/模式匹配检查。您可以使用模

我有这个功能:

def update(%Evento{} = evento, attrs, dataSchema) do
    evento
    |> dataSchema.changeset(attrs)
    |> Repo.update()
end
它与
%Evento{}
结构绑定。

我想让它独立于结构,而是传递一个参数,这样在调用函数时,我可以传递
%Evento{}
%News{}
%Contact{}
或任何我想要的结构,同时保持相同的功能/模式匹配检查。

您可以使用模式
%\u{}
接受任何结构作为参数:

def update(%_{} = struct, attrs, dataSchema) do
  ...
end
或者,您可以使用模式
%module{}
和保护接受一组白名单结构:

def update(%module{} = struct, attrs, dataSchema) when module in [Evento, Foo, Bar] do
  ...
end

编辑:更新为使用新的
%module{}
模式,如Łukasz Niemier在评论中建议的那样

您可以使用模式
%\u{}
接受任何结构作为参数:

def update(%_{} = struct, attrs, dataSchema) do
  ...
end
或者,您可以使用模式
%module{}
和保护接受一组白名单结构:

def update(%module{} = struct, attrs, dataSchema) when module in [Evento, Foo, Bar] do
  ...
end

编辑:更新为使用新的
%module{}
模式,如Łukasz Niemier在评论中建议的那样

虽然@Dogbert的答案[像往常一样]完美且自我解释,但我想在这里提出一种更麻烦的方法,允许回调不同类型的输入模块,仍然是100%干燥的:

defmodule DryStructMatch do
  defmacrop clause!(name, mod, fun) do
    quote bind_quoted: [name: name, mod: mod, fun: fun] do
      ast = {:%, [], [{:__aliases__, [alias: false], [mod]}, {:%{}, [], []}]}
      quote do
        def unquote(name)(unquote(ast) = struct, _arg1, _arg2) do
          result = struct # |> ... COMMON BLOCK
          unquote(:"#{name}_callback")(unquote(fun), result)
        end
      end
    end
  end

  @doc ~S"""
  Usage:

      use DryStructMatch, update: [Foo, Bar: &IO.inspect/1]

  The above will be expanded into two `update` clauses, the former having
    no callback, and the latter having a callback that spits the result
    out to console before returning it (due to `&IO.inspect/1`.)
  """
  defmacro __using__(opts) do
    Enum.flat_map(opts, fn {name, mods} ->
      [
        quote do
          defp unquote(:"#{name}_callback")(fun, result)
            when is_function(fun, 1), do: fun.(result)
          defp unquote(:"#{name}_callback")(_, result), do: result

          def unquote(name)(struct, _arg1 \\ nil, _arg2 \\ nil)
        end |

        Enum.map(mods, fn
          {mod, fun} -> clause!(name, mod, fun)
          mod -> clause!(name, mod, nil)
        end)
      ]
    end)
  end
end
我们在这里要做的是:我们使用\uuuuu(opts)宏声明调用
\uu的参数中指定的尽可能多的子句。为了简单起见,此示例不允许传递公共
(它是硬编码的),但也可以很容易地修改接受不同公共块的代码

让我们测试一下:

defmodule Foo, do: defstruct foo: 42
defmodule Bar, do: defstruct bar: 42
defmodule Baz, do: defstruct baz: 42

defmodule A do
  use DryStructMatch, update: [Foo, Bar: &IO.inspect/1]
end

defmodule Test do
  def test do
    IO.inspect A.update(%Foo{}) # prints %Foo{foo: 42} (explicit)
    A.update(%Bar{})            # prints %Bar{bar: 42} (callback)
    A.update(%Baz{})            # raises `FunctionClauseError`
  end
end

Test.test
后者将成功调用
Test.Test/0
中第一行和第二行的
update
,第三行调用失败:

%Foo{foo: 42}
%Bar{bar: 42}

** (FunctionClauseError) no function clause matching in A.update/3    

    The following arguments were given to A.update/3:

        # 1
        %Baz{baz: 42}

        # 2
        nil

        # 3
        nil

    iex:17: A.update/3

希望这能有所帮助。

虽然@Dogbert的答案[像往常一样]完美且自我解释,但我会在这里提出一种更麻烦的方法,允许回调不同类型的输入模块,仍然是100%干燥的:

defmodule DryStructMatch do
  defmacrop clause!(name, mod, fun) do
    quote bind_quoted: [name: name, mod: mod, fun: fun] do
      ast = {:%, [], [{:__aliases__, [alias: false], [mod]}, {:%{}, [], []}]}
      quote do
        def unquote(name)(unquote(ast) = struct, _arg1, _arg2) do
          result = struct # |> ... COMMON BLOCK
          unquote(:"#{name}_callback")(unquote(fun), result)
        end
      end
    end
  end

  @doc ~S"""
  Usage:

      use DryStructMatch, update: [Foo, Bar: &IO.inspect/1]

  The above will be expanded into two `update` clauses, the former having
    no callback, and the latter having a callback that spits the result
    out to console before returning it (due to `&IO.inspect/1`.)
  """
  defmacro __using__(opts) do
    Enum.flat_map(opts, fn {name, mods} ->
      [
        quote do
          defp unquote(:"#{name}_callback")(fun, result)
            when is_function(fun, 1), do: fun.(result)
          defp unquote(:"#{name}_callback")(_, result), do: result

          def unquote(name)(struct, _arg1 \\ nil, _arg2 \\ nil)
        end |

        Enum.map(mods, fn
          {mod, fun} -> clause!(name, mod, fun)
          mod -> clause!(name, mod, nil)
        end)
      ]
    end)
  end
end
我们在这里要做的是:我们使用\uuuuu(opts)
宏声明调用
\uu的参数中指定的尽可能多的子句。为了简单起见,此示例不允许传递公共
(它是硬编码的),但也可以很容易地修改接受不同公共块的代码

让我们测试一下:

defmodule Foo, do: defstruct foo: 42
defmodule Bar, do: defstruct bar: 42
defmodule Baz, do: defstruct baz: 42

defmodule A do
  use DryStructMatch, update: [Foo, Bar: &IO.inspect/1]
end

defmodule Test do
  def test do
    IO.inspect A.update(%Foo{}) # prints %Foo{foo: 42} (explicit)
    A.update(%Bar{})            # prints %Bar{bar: 42} (callback)
    A.update(%Baz{})            # raises `FunctionClauseError`
  end
end

Test.test
后者将成功调用
Test.Test/0
中第一行和第二行的
update
,第三行调用失败:

%Foo{foo: 42}
%Bar{bar: 42}

** (FunctionClauseError) no function clause matching in A.update/3    

    The following arguments were given to A.update/3:

        # 1
        %Baz{baz: 42}

        # 2
        nil

        # 3
        nil

    iex:17: A.update/3

希望这会有帮助。

为什么不从模式中删除
%Evento{}=
?如果您只想接受结构,那么
def update(%{{uuuuuu struct\uuuu:}=struct,attrs,dataSchema)执行
。还是我误解了什么?@Dogbert你说得对。我只想接受structs。你能把这变成一个答案,这样我就可以接受了吗?为什么不从模式中删除
%Evento{}=
?如果您只想接受结构,那么
def update(%{{uuuuuu struct\uuuu:}=struct,attrs,dataSchema)执行
。还是我误解了什么?@Dogbert你说得对。我只想接受structs。你能把这变成一个答案,这样我就可以接受了吗?我个人认为
%module{}
语法比
%{{code>结构:module}
@ukaszNiemier谢谢,我忘了最近在长生不老药中添加了这些!我已经更新了答案。我个人认为
%module{}
语法比
%{{uuuuuu struct\uuuuuuu:module}
@ukaszNiemier谢谢,我忘了最近在Elixir中添加了这些语法!我已经更新了答案。