能否重载Elixir中Map的订阅运算符以适配Decimal键?
用Decimal作为Map键的相等性处理方案
首先明确:没法直接重载Map的订阅运算符[],Elixir的内置Map结构中,[]的键匹配逻辑固定基于==,无法修改这个行为。不过有几个可行的替代方案:
1. 自定义包装模块封装Map操作
自己写一个模块,把Map的读写操作封装起来,内部用Decimal.eq?/2判断键的相等性:
defmodule DecimalMap do def new, do: %{} # 存入键值对时,先移除逻辑相等的旧键 def put(map, key, value) when is_map(map) and is_struct(key, Decimal) do existing_key = Enum.find(map, fn {k, _} -> Decimal.eq?(k, key) end) map = if existing_key, do: Map.delete(map, elem(existing_key, 0)), else: map Map.put(map, key, value) end # 读取时用Decimal.eq?匹配键 def get(map, key) when is_map(map) and is_struct(key, Decimal) do Enum.find_value(map, fn {k, v} -> Decimal.eq?(k, key) && v end) end end
用法示例:
map = DecimalMap.new() map = DecimalMap.put(map, Decimal.new(3), "three") DecimalMap.get(map, Decimal.from_float(3.0)) # 返回 "three"
2. 用宏简化查找逻辑
写一个宏,把对Decimal键的查找自动转换成基于Decimal.eq?的遍历,不用每次手动写Enum.find:
defmodule DecimalMapMacro do defmacro dm_get(map, key) do quote do Enum.find_value(unquote(map), fn {k, v} -> cond do is_struct(k, Decimal) and is_struct(unquote(key), Decimal) -> Decimal.eq?(k, unquote(key)) && v true -> k == unquote(key) && v end end) end end end
用法示例:
import DecimalMapMacro map = %{Decimal.new(3) => "three"} dm_get(map, Decimal.from_float(3.0)) # 返回 "three"
3. 预规范化Decimal键
存入Map前,对所有Decimal键做规范化处理,让逻辑相等的Decimal变成结构上==相等的实例,这样就能直接用Map的默认订阅运算符:
# 用Decimal.normalize/1规范化键 key1 = Decimal.new(3) |> Decimal.normalize() key2 = Decimal.from_float(3.0) |> Decimal.normalize() map = %{key1 => "three"} map[key2] # 返回 "three",因为规范化后两个结构体==相等
注意:这个方案依赖Decimal.normalize/1的效果,大部分场景下能让逻辑相等的Decimal统一结构,但如果涉及特殊精度配置,可能需要额外调整规范化规则。
内容的提问来源于stack exchange,提问作者cjm2671
相关产品推荐
相关产品推荐

