长生不老药真的让我大吃一惊,并使语言如此混乱的使用。我需要迭代一个嵌套的映射并根据迭代简单地更新一些计数,但是Enum.reduce
只会给我带来困难。说我有:
defmodule Predictor do
def past_matches() do
[
team1: %{team2: %{f: 0, a: 1}, team3: %{f: 1, a: 3}},
team2: %{team1: %{f: 3, a: 0}, team3: %{f: 2, a: 0}},
team3: %{team1: %{f: 1, a: 0}, team2: %{f: 0, a: 1}},
]
end
def init_indexes(matches) do
local = Enum.reduce matches, %{}, fn({team, _scores}, acc) ->
Map.put acc, team, %{f: 0, a: 0, n_games: 0}
end
Enum.each matches, fn({team, scores}) ->
Enum.each scores, fn({vteam, %{f: ff, a: aa}}) ->
%{f: fi, a: ai, n_games: ni} = local[team]
put_in(local[team], %{f: fi+ff, a: ai+aa, n_games: ni+1})
end
end
local
end
def run() do
local = past_matches() |> init_indexes()
end
end
我需要local
把f
,a
和n_games
相加。
local = %{
team1: %{f: 1, a: 4, n_games: 2}
...
}
显然,在run()
的末尾,Map local
具有所有的0's值,并且没有更新的值。
发布于 2017-06-05 00:37:41
有几件事我可以从球棒上看到,它们会让你绊倒:
Enum.each/2
将对列表中的每个项应用一个函数,但它不会以任何方式累积结果或修改原始列表。我不记得我最后一次使用Enum.each
是什么时候--它有它的位置,但它非常罕见。local[team]
的地方实际上没有更新任何值,因为输出没有传递到其他任何地方。从本质上说,这只是将这些变化转化为以太。|>
管道工会改变你的生活。如果你是一个OO背景,但坚持下去,我保证你再也不想回去了。让我头脑清醒的是,最初尽可能少地使用匿名函数是思想上的转变。它帮助我适应了不变的概念,因为它迫使我思考每个函数实际需要什么值,以及如何将这些值传递给每个函数。下面是我尝试用一种更以管道为中心的方法重写您的模块--希望它会有所帮助。在IEx中运行它时,它会产生预期的结果。如果你有问题,我很乐意澄清任何事情。
defmodule Predictor do
@past_matches [
team1: %{
team2: %{f: 0, a: 1},
team3: %{f: 1, a: 3}
},
team2: %{
team1: %{f: 3, a: 0},
team3: %{f: 2, a: 0}
},
team3: %{
team1: %{f: 1, a: 0},
team2: %{f: 0, a: 1}
}
]
# see note 1
@baseline %{f: 0, a: 0, n_games: 0}
def run(past_matches \\ @past_matches) do
past_matches
|> Enum.map(&build_histories/1)
|> Enum.into(%{})
# see note 2
end
def build_histories({team, scores}) do
history = Enum.reduce(scores, @baseline, &build_history/2)
{team, history}
end
def build_history({_vteam, vresults}, acc) do
# see note 3
%{acc | f: acc.f + vresults.f,
a: acc.a + vresults.a,
n_games: acc.n_games + 1}
end
end
(1) since the baseline is the same for every team, you can
set it as a module attribute -- basically like setting a global
(immutable) variable that you can use as a starting point for a new
value. Another option would be to create a %BaseLine{} struct that
has default values.
(2) you could also use `Enum.reduce/2` here instead, but this does
effectively the same thing -- the output of the `Enum.map/1`
call is a list of {atom, _val} which is interpreted as a Keyword
list; calling `Enum.into(%{}) turns a Keyword list into a map
(and vice versa with `Enum.into([])`).
(3) NB: %{map | updated_key: updated_val} only works on maps or
structs where the key to be updated already exists -- it'll throw
an error if the key isn't found on the original map.
https://stackoverflow.com/questions/44359447
复制相似问题