用理解来确定问题的范围

时间:2016-08-21 09:26:41

标签: elixir

我想对一系列价值组合进行一些分析。

我有以下函数,但由于某种原因,在完成了理解之后并且在函数体的末尾,变量analytics仍然是一个空列表,而它不在每次迭代的理解之内

有什么想法吗?

def handle_cast({:start}, state) do
    intervals = [7, 30, 90]
    groupings = ["day_of_week", "time_of_day"]
    aggregators = [
      %{
        domain: "support",
        metric: "new_conversations",
        func:   &App.get_new_conversations/2
      },
      %{
        domain: "support",
        metric: "closed_conversations",
        func:   &App.get_closed_conversations/2
      },
      %{
        domain: "support",
        metric: "median_response_time",
        func:   &App.get_median_response_time/2
      },
    ]

    Repo.transaction(fn ->
      Repo.delete_all(Analytic)

      analytics = []
      for interval <- intervals do
        for grouping <- groupings do
          for %{domain: domain, metric: metric, func: func} <- aggregators do
            analytic =
              func.(grouping, interval)
              |> Enum.map(fn %{"app_id" => app_id, "data" => data} = result ->
                %Analytic{app_id: app_id, domain: domain, metric: metric, grouping: grouping, interval_in_days: interval, data: data}
              end)

            analytics = [analytic|analytics]
          end
        end
      end
    end)

    {:noreply, state}
  end

1 个答案:

答案 0 :(得分:3)

Elixir中的变量是不可变的但可重新绑定。这意味着行analytics = [analytic|analytics]正在创建一个新列表并将其绑定到该块范围的名为analytics的变量。当块结束时,更改不会在for的下一次迭代中保留。例如:

iex(1)> x = 1
1
iex(2)> for i <- 1..3 do
...(2)>   IO.puts(x); x = x + i; IO.puts(x)
...(2)> end
1
2
1
3
1
4

对于您编写的代码,您可以使用for返回其中最后一个表达式的值列表的事实,并将最外层for的返回值存储到analytics,但是存在轻微问题:您最终会遇到嵌套列表:

iex(1)> for i <- 1..2 do
...(1)>   for j <- 1..2 do
...(1)>     for k <- 1..2 do
...(1)>       {i, j, k}
...(1)>     end
...(1)>   end
...(1)> end
[[[{1, 1, 1}, {1, 1, 2}], [{1, 2, 1}, {1, 2, 2}]],
 [[{2, 1, 1}, {2, 1, 2}], [{2, 2, 1}, {2, 2, 2}]]]

但是,这是一个简单的解决方案! for在一次调用中接受多个<-子句,并自动返回一个平面列表:

iex(1)> for i <- 1..2, j <- 1..2, k <- 1..2 do
...(1)>   {i, j, k}
...(1)> end
[{1, 1, 1}, {1, 1, 2}, {1, 2, 1}, {1, 2, 2}, {2, 1, 1}, {2, 1, 2}, {2, 2, 1},
 {2, 2, 2}]

使用此方法,您的代码变为:

analytics =
  for interval <- intervals,
      grouping <- groupings,
      %{domain: domain, metric: metric, func: func} <- aggregators do
      func.(grouping, interval)
      |> Enum.map(fn %{"app_id" => app_id, "data" => data} = result ->
        %Analytic{app_id: app_id, domain: domain, metric: metric, grouping: grouping, interval_in_days: interval, data: data}
      end)
    end
  end
end

这应该会为您提供与原始代码相同的输出。

相关问题