Elixir 具有可选键的模式匹配映射

Elixir 具有可选键的模式匹配映射,elixir,Elixir,我有一个函数,它接收带有多个键的地图,其中一些键是可选的。如何编写包含映射的函数签名,同时允许可选键默认为某个值 def handle_my_map(%%{text:text, 打印次数:打印次数,#我希望默认为2 颜色:颜色#我希望默认为“蓝色” })做 枚举每个(1..print_次,fn()->IO.puts[“(”,color,“):”,text]end) 结束 测试。处理我的地图(%{text:“here”,打印次数:5,颜色:“Red”}) #(红色):这里 #(红色):这里 #(红

我有一个函数,它接收带有多个键的地图,其中一些键是可选的。如何编写包含映射的函数签名,同时允许可选键默认为某个值

def handle_my_map(%%{text:text,
打印次数:打印次数,#我希望默认为2
颜色:颜色#我希望默认为“蓝色”
})做
枚举每个(1..print_次,fn()->IO.puts[“(”,color,“):”,text]end)
结束
测试。处理我的地图(%{text:“here”,打印次数:5,颜色:“Red”})
#(红色):这里
#(红色):这里
#(红色):这里
#(红色):这里
#(红色):这里
处理我的映射(%{text:“there”})
#=>匹配错误!
我希望是:

处理我的地图(%{text:“where”,打印时间:3})
#(蓝色):在哪里
#(蓝色):在哪里
#(蓝色):在哪里
处理我的映射(%{text:“there”})
#(蓝色):那里
#(蓝色):那里
类似于ruby的关键字参数:

def handle_my_map(文本:nil,打印次数:2,颜色:'Blue')

很好。我认为您需要编写另一个
handle\u my\u map
函数,该函数带有参数
%{a:a,b:b}
。像这样:

  def handle_my_map(%{a: a, b: b, optional_c: c}) do
    a + b + c
  end

  def handle_my_map(%{a: a, b: b}) do
    a + b
  end

  YourModule.handle_my_map %{a: 1, b: 2}
  #=> 3
  YourModule.handle_my_map %{a: 1, b: 2, optional_c: 3}
  #=> 6

Elixir将搜索一个函数
handle\u my\u map
,该函数与您的参数匹配,直到具有arity 1 end的函数为止

我可能会选择这样的函数:

defmodulehandler-do
@默认值%{打印次数:2,颜色:“蓝色”}
def handle_my_map(映射)do
%{text:text,print_times:times,color:color}=Dict.put_new(map,@defaults)
枚举每个(1..次,fn()->IO.puts[“(”,color,“):”,text]end)
结束
结束
如果需要使用现有键处理
nil
值,可以执行以下操作:

defmodulehandler-do
@默认值%{打印次数:2,颜色:“蓝色”}
def handle_my_map(映射)do
%{text:text,print_times:times,color:color}=@defaults
|>Dict.merge(映射)
|>Enum.into%{},fn
{key,nil}->{key,@defaults[key]}
{key,val}->{key,val}
结束
枚举每个(1..次,fn()->IO.puts[“(”,color,“):”,text]end)
结束
结束

您可以使用
映射。合并/2

defmodulehandler-do
@默认值%{打印次数:2,颜色:“蓝色”}
def handle_my_map(映射)do
%{text:text,print_times:times,color:color}=merge_默认值(映射)
枚举每个(1..次,fn()->IO.puts[“(”,color,“):”,text]end)
结束
defp merge_默认值(映射)do
Map.merge(@defaults,Map)
结束
结束
如果要允许nils,可以使用
Map.merge/3
并将
merge\u defaults/1
更改为:

defp merge_默认值(map)do
合并(@defaults,Map,fn _键,default,val->val | | default end)
结束

处理我的映射/1
接受一个参数?是,一个映射参数是,但是如果有5个可选参数呢?我需要32个(
2^5
)实现…所以您不能使用模式匹配。您可以这样做:
def handle\u my\u map(map),do:map[:a]+map[:b]+zero\u if\u nil(map[:optional\u c])
,其中
zero\u if\u nil/1
是另一个帮助函数。遗憾的是
def方法(%%{a:a,b:b,c:c\\default c})不起作用(编辑以回答您修改后的问题:)谢谢,我来试试。虽然它看起来有点冗长,但如果不止一个位置需要它,那么它是宏的一个很好的候选者……这种任务的代码生成是不必要的。为了可重用性,我会将其提取到带有默认值的
merge\u(我的\u映射,默认值)
func。您应该使用Dict.merge/3而不是Enum.into/3。我本来打算添加它作为评论,但它太大了,所以我添加了另一个答案。谢谢,我会尝试的。您是否还有深度合并的建议?如果所有值都是映射,您可以执行类似于此处所做的操作:如果不是,您只需要检查两个值是否都是映射,然后调用Map.merge/2。要澄清,“允许nils”意味着用默认值覆盖nils。顺便说一句,这个答案对于
Map.merge
来说是一个不错的文档,因为该文档在Elixir安装中还不存在。@Urigassi:in中的
setVals
方法充当执行深度合并的镜头。免责声明,我刚刚写了它,但在试图找到现有的实现时,首先在这里搜索。也就是说,José建议的非镜头实现可能更简单。