使用单个模式匹配匹配空列表和非空列表?
Matching empty and non-empty lists with a single pattern match?
我有以下函数,它接受映射中的一堆列表(目前是 2 个),returns 列表只包含每个列表的第一个元素:
transform_remove_history(#{
prematchVars := [#{vars := LatestPrematchVars} | _],
vars := [#{vars := LatestVars} | _]}
= GameState) ->
GameState#{prematchVars := [LatestPrematchVars], vars := [LatestVars]};
transform_remove_history(GameState) ->
GameState.
(地图还有很多其他元素,不仅仅是这两个列表!)
问题是,如果 prematchVars
或 vars
是空列表,则第一个模式不匹配,两个列表都不会更新。
如果我有 N
列表,那么我必须编写的模式将是所有可能的组合(如果我的数学是正确的,那就是 2^N ......好吧,无论如何有很多组合).
我可以为列表转换编写一个辅助函数,但我想知道在 Erlang 中是否有一种有效的方式将其表示为单个模式匹配?
您可以使用 maps:map/2
来处理每个地图元素并生成新地图:
transform_remove_history(GameState) ->
maps:map(fun(_,[]) ->
[];
(K,[H|_]) when K =:= prematchVars; K =:= vars ->
[H];
(_,V) ->
V
end, GameState).
映射函数有两个参数,键和值。如果值为[]
,就return而已;如果它是一个或多个元素的列表,return 一个只包含头部的列表;否则只是 return 原样的值。如果您只需要匹配某些键,您可以通过为映射函数的第一个参数指定匹配来实现。
你选择的数据结构有点奇怪。基本上,这听起来就像您想对任意列表堆栈执行映射操作(如 lists:map/2
)。
似乎给定您想要的任何特定列表:
truncate([]) -> [];
truncate([H|_]) -> [H].
然后您希望这发生在地图中的所有元素上。这为我们提供了截断函数的地图友好版本,以及对 maps:map/2
:
的调用
truncate(_, []) -> [];
truncate(_, [H|_]) -> [H].
Truncated = maps:map(fun truncate/2, Map).
或非常相似的东西,具体取决于您的实际数据结构的外观。关于上周在 ML 上对地图的列表操作的讨论,无论如何,它们中的大多数都是作为对 maps:to_list/1
输出的实际列表操作实现的,因此保持 GameState
可能没有任何优势地图中的结构。
关于键的特定子集
如果您绝对被迫处理一个数据结构,该数据结构相当于一大堆粗心大意的废话,全部塞进一个映射中(在最初由 Erlang 新手编写的遗留代码)您可以通过使用 maps:with/2
:
生成键列表并为您的操作做准备来节省一些时间
Keys = [foo, bar, baz],
Partial = maps:with(Keys, Map),
Truncated = maps:map(fun truncate/2, Partial).
通过这种方式,您可以只处理您关心的部分,而将大量不相关的废话排除在您要执行的操作之外。
不过,从长远来看,让您的生活(以及未来维护者的生活)更轻松的最好方法是开始将您的大图结构抽象成一个它自己的 ADT。创建一个名为 game_data
的模块,编写等同于 getter 和 setter 的函数,在这些函数的后面,您可以根据需要使数据看起来 并根据需要更改布局 而不会导致更改为每次都遍历整个代码库。
我有以下函数,它接受映射中的一堆列表(目前是 2 个),returns 列表只包含每个列表的第一个元素:
transform_remove_history(#{
prematchVars := [#{vars := LatestPrematchVars} | _],
vars := [#{vars := LatestVars} | _]}
= GameState) ->
GameState#{prematchVars := [LatestPrematchVars], vars := [LatestVars]};
transform_remove_history(GameState) ->
GameState.
(地图还有很多其他元素,不仅仅是这两个列表!)
问题是,如果 prematchVars
或 vars
是空列表,则第一个模式不匹配,两个列表都不会更新。
如果我有 N
列表,那么我必须编写的模式将是所有可能的组合(如果我的数学是正确的,那就是 2^N ......好吧,无论如何有很多组合).
我可以为列表转换编写一个辅助函数,但我想知道在 Erlang 中是否有一种有效的方式将其表示为单个模式匹配?
您可以使用 maps:map/2
来处理每个地图元素并生成新地图:
transform_remove_history(GameState) ->
maps:map(fun(_,[]) ->
[];
(K,[H|_]) when K =:= prematchVars; K =:= vars ->
[H];
(_,V) ->
V
end, GameState).
映射函数有两个参数,键和值。如果值为[]
,就return而已;如果它是一个或多个元素的列表,return 一个只包含头部的列表;否则只是 return 原样的值。如果您只需要匹配某些键,您可以通过为映射函数的第一个参数指定匹配来实现。
你选择的数据结构有点奇怪。基本上,这听起来就像您想对任意列表堆栈执行映射操作(如 lists:map/2
)。
似乎给定您想要的任何特定列表:
truncate([]) -> [];
truncate([H|_]) -> [H].
然后您希望这发生在地图中的所有元素上。这为我们提供了截断函数的地图友好版本,以及对 maps:map/2
:
truncate(_, []) -> [];
truncate(_, [H|_]) -> [H].
Truncated = maps:map(fun truncate/2, Map).
或非常相似的东西,具体取决于您的实际数据结构的外观。关于上周在 ML 上对地图的列表操作的讨论,无论如何,它们中的大多数都是作为对 maps:to_list/1
输出的实际列表操作实现的,因此保持 GameState
可能没有任何优势地图中的结构。
关于键的特定子集
如果您绝对被迫处理一个数据结构,该数据结构相当于一大堆粗心大意的废话,全部塞进一个映射中(在最初由 Erlang 新手编写的遗留代码)您可以通过使用 maps:with/2
:
Keys = [foo, bar, baz],
Partial = maps:with(Keys, Map),
Truncated = maps:map(fun truncate/2, Partial).
通过这种方式,您可以只处理您关心的部分,而将大量不相关的废话排除在您要执行的操作之外。
不过,从长远来看,让您的生活(以及未来维护者的生活)更轻松的最好方法是开始将您的大图结构抽象成一个它自己的 ADT。创建一个名为 game_data
的模块,编写等同于 getter 和 setter 的函数,在这些函数的后面,您可以根据需要使数据看起来 并根据需要更改布局 而不会导致更改为每次都遍历整个代码库。