You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

参考JS实现,如何在Elixir中用Map结合不可变性统计字符次数?

问题:Elixir中基于不可变性用Map统计字符出现次数的最佳实现?

在JavaScript中,我可以通过以下代码统计字符出现次数:

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")); //返回 { e: 1, h: 1, l: 2, o: 1 }

请问考虑到Elixir的不可变性特性,使用Map统计每个字符出现次数的最佳实现方式是什么?


回答

在Elixir里,所有数据都是不可变的,我们没法像JS那样直接修改对象属性,得用函数式的累加思路来处理统计需求,最常用也最优雅的方式就是借助Enum.reduce/3,它能帮我们遍历字符序列,每次返回一个新的Map来记录统计结果,完美契合不可变性的设计。

基础优雅实现

这是最贴近需求逻辑的实现,可读性和简洁性都拉满:

def count_it(str) do
  String.graphemes(str)
  |> Enum.reduce(%{}, fn char, acc ->
    Map.update(acc, char, 1, &(&1 + 1))
  end)
end

# 测试调用
IO.inspect(count_it("hello")) # 输出 %{"e" => 1, "h" => 1, "l" => 2, "o" => 1}

我拆解下关键步骤:

  1. String.graphemes(str):把字符串拆成单个字符的列表,比如"hello"会变成["h", "e", "l", "l", "o"],这一步是为了让字符串能被Enum模块处理。
  2. Enum.reduce/3:第一个参数是待遍历的字符列表,第二个是初始累加器(空Map%{}),第三个是处理每个字符的匿名函数。
  3. Map.update/4:这个函数简直是为统计场景量身定做的——如果Map里没有当前字符,就把它的计数设为1;如果已经存在,就把对应的值加1。这里的&(&1 + 1)是Elixir的简写匿名函数,等价于fn current_value -> current_value + 1 end。

模式匹配风格实现

如果你想更贴近函数式的模式匹配思路,也可以这么写,逻辑和上面完全一致,只是写法不同:

def count_it(str) do
  String.graphemes(str)
  |> Enum.reduce(%{}, fn
    char, acc when is_map_key(acc, char) ->
      Map.put(acc, char, acc[char] + 1)
    char, acc ->
      Map.put(acc, char, 1)
  end)
end

这里通过对累加器的模式匹配,判断字符是否已存在,分别处理两种情况,适合喜欢用模式匹配表达逻辑的开发者。

小提醒

  • Elixir里String.graphemes返回的是字符串类型的单个字符(比如"h"),所以统计的键都是字符串。如果你想用字符码(比如?h对应ASCII码104)作为键,可以用String.to_charlist(str)来拆分字符串。
  • 不可变性的好处:每次Map.update或Map.put都会返回新的Map,原Map不会被修改,这避免了并发场景下的竞态问题,也让代码更安全、可预测。

内容的提问来源于stack exchange,提问作者Iggy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 08:33:41