计算每个炭(Elixir)的发生次数?

时间:2018-04-02 12:53:44

标签: elixir

在JS中,我可以这样做:

function countIt(str){
  let obj = {};
  for(let i = 0; i < str.length; i++){
    if(!obj[str[i]]){
      obj[str[i]] = 1;
    } else {
      obj[str[i]]++;
    }
  }
  return obj;
}

console.log(countIt("hello"));
//returns {
  e: 1,
  h: 1,
  l: 2,
  o: 1
}

考虑到其不变性,在Elixir中用Map计算每个角色的出现次数的最佳方法是什么?函数式语言解决此类问题的一般策略是什么?

4 个答案:

答案 0 :(得分:8)

  

函数式语言解决此类问题的一般策略是什么?

<强> Enum.reduce/3

"Hello World"
|> String.graphemes()
|> Enum.reduce(%{}, fn char, acc ->
     Map.put(acc, char, (acc[char] || 0) + 1)
   end)
#⇒ %{" " => 1, "H" => 1, "W" => 1, "d" => 1,
#    "e" => 1, "l" => 3, "o" => 2, "r" => 1}

或(使用Map.update/4信用@Dogbert:

"Hello World"
|> String.graphemes()
|> Enum.reduce(%{}, fn char, acc ->
     Map.update(acc, char, 1, &(&1 + 1))
   end)

或者,也许更惯用,基于模式匹配reducer参数来确定我们是否应该添加或启动计数器:

"Hello World"
|> String.graphemes()
|> Enum.reduce(%{}, fn char, acc ->
     case acc do
       %{^char => count} -> %{acc | char => count + 1}
       _ -> Map.put(acc, char, 1)
     end
   end)

答案 1 :(得分:1)

使用Elixir-1.10,您可以在一行中使用Enum.frequencies函数来完成此操作?

"Hello World" |> String.graphemes |> Enum.frequencies

答案 2 :(得分:0)

另一种可以实现此目的的方法是使用尾递归函数。此功能不再向堆栈添加任何帧,因为在运行期间,该功能将返回到自身的顶部而不是再次调用。为了实现这一点,你需要有一个累加器函数,如_count

defmodule Kitten do
  def count(word) do
    word
    |> _count
  end

  defp _count("", acc), do: acc

  defp _count(<<head::utf8, tail::binary>>, acc \\ %{}) do
    updated_acc = Map.update(acc, <<head>>, 1, &(&1 + 1))
    _count(tail, updated_acc)
  end
end

答案 3 :(得分:0)

  

考虑到不可变性,使用Elixir中的Map计算每个字符出现的最佳方法是什么?函数式语言解决此类问题的一般策略是什么?

您可以使用Map.new/2并获得更简洁的代码:

count.exs

defmodule Count do
  def count_it(str) do
    str
      |> Map.new( &{<<&1 :: utf8>>,
         Enum.count(str,fn x -> &1 == x end)}
      )
  end
end

使用进行测试:iex count.exs

iex(1)> Count.count_it('hello')
%{"e" => 1, "h" => 1, "l" => 2, "o" => 1}

iex(2)>