当我调用以下函数时,关键字列表1中的{:a,2}永远不会被转移,但是列表2中的{:b,4}被转移到结果列表,即使没有匹配的关键字列表1。谁知道为什么?
iex> Keyword.merge([a: 1, a: 2, b: 2], [a: 3, b: 3, b: 4], fn _k, v1,v2->v1+v2 end)
[a: 4, b: 5, b: 4]
更让我感到奇怪的是,在下面的合并{:a,2}被转移到结果列表中
iex>Keyword.merge([a: 1, a: 2, b: 2], [b: 3, b: 4], fn _k, v1,v2->v1+v2 end)
[a: 1, a: 2, b: 5, b: 4]
这是一个棘手的问题。
TL; DR:这样做是为了使它与Keyword.merge/2
保持一致。
这篇来自Elixir tests的摘录证明了这个问题:
fun = fn _key, _value1, value2 -> value2 end [...lots of arg1 and arg2 declarations...] assert Keyword.merge(arg1, arg2) == Keyword.merge(arg1, arg2, fun)
修复可能很简单:只需s/delete/delete_first
here:
- do_merge(tail, acc, delete(rest, key), original, fun, keywords2)
+ do_merge(tail, acc, delete_first(rest, key), original, fun, keywords2)
但是这样,正常的合并功能(没有解析器)的行为会有所不同。我将检查Keyword.merge/2
是否可能适应预期的行为,并可能填补错误/提供PR。
FWIW:Josz关闭https://github.com/elixir-lang/elixir/issues/7420说:
引用文档:
关键字可能具有重复的键,因此它不是严格意义上的键值存储。 但是,此模块中的大多数函数都与字典完全相同,因此它们的工作方式与
Map
模块中的函数类似。在这种情况下,这意味着右侧的内容确实会删除左侧的所有内容。与
Keyword.put
删除所有其他条目的方式相同,而不是简单地添加到关键字列表中。 :)
这是正确的。在第一个例子中,
A = [a: 1, a: 2, b: 2]
B = [a: 3, b: 3, b: 4]
Keyword.merge(A, B, fn _k, v1,v2->v1+v2 end)
B
的{a: 3}
将匹配A中的第一个a,因此fn
将适用于解决冲突。 A
的第二个a
没有比赛,将被丢弃。然后,B
的{b: 3}
将匹配A
的{b: 2}
,也将应用fn
来解决冲突。
注意
包括在关键字2中给出的重复密钥将被添加到keywords1
,B
的{b:4}
没有匹配,它将添加到A.
因此结果是[a: 4, b: 5]
添加{b: 4}
,这是[a: 4, b: 5, b: 4]
。
第二个例子,
A = [a: 1, a: 2, b: 2]
B = [b: 3, b: 4]
Keyword.merge(A, B, fn _k, v1,v2->v1+v2 end)
因为在B
中没有匹配的键,所以A
的[a:1, a:2]
将不会超越和保留。
B
的{b:3}
将匹配A
的{b:2}
,所以使用fn
解决冲突,res是{b: 5}
。 {b: 4}
没有比赛,将被保留。
所以res是[a: 1, a: 2, b: 5, b: 4]
。
您还可以查看Keyword.merge
文档的示例。
Keyword.merge([a: 1, b: 2, a: 3], [a: 3, d: 4, a: 5], fn :a, v1, v2 ->
...> v1 + v2
...> end)
对于这个,a
和b
有两场比赛,将逐一申请。 res是[b: 2, a: 4, d: 4, a: 8]
。