[英]Can I overload the subscription operator for Map in Elixir?
假設我有一個 map %{}
,其中的鍵是Decimal
。
有問題的是,在 Decimal 3.= 3.0
中,因此,對 Decimal 鍵的索引是不可靠的,需要使用Decimal.eq?/2
來測試相等性。
有沒有一種方法可以重載 map 訂閱運算符,以便 Decimal 上的索引實際使用eq?
相反==
?
即mymap[Decimal.new(3)] == mymap[Decimal.from_float(3.0)]
如果我遺漏了另一種方法(例如協議/宏/其他東西,請告訴我!)
不幸的是,即使 Elixir 提供了許多擴展語言的方法(協議/宏...),映射是虛擬機提供的底層構造,您將無法覆蓋此行為。 映射需要依賴一些鍵的散列,而不僅僅是比較 function。
適用於您的用例的是首先使用Decimal.normalize/1
規范化您的密鑰(在放置和訪問之前),以便兩者的密鑰相同:
iex> Decimal.new(3) |> Decimal.normalize()
#Decimal<3>
iex> Decimal.from_float(3.0) |> Decimal.normalize()
#Decimal<3>
您可以將其包裝在一個模塊中以進行頻繁操作:
defmodule DecimalMap do
def put(map, key, value), do: Map.put(map, Decimal.normalize(key), value)
def fetch(map, key), do: Map.fetch(map, Decimal.normalize(key))
# ...
end
iex> map = DecimalMap.put(%{}, Decimal.from_float(3.0), "hello")
%{#Decimal<3> => "hello"}
iex> DecimalMap.fetch(map, Decimal.new(3))
{:ok, "hello"}
我認為這里有一些誤解。
[]
,這就是Access 語法。Decimal
是一種結構,結構通常帶有額外的語義,這意味着您不能依賴結構的底層底層形狀,您必須調用模塊中的函數 ( eq?
) 來提取含義。 如果您將Decimal
用作 map 鍵,則隱含地依賴於它的結構而不是語義,這在這種情況下是無效的,因為不同的Decimal
結構可以被認為在語義上是等價的。Decimal.from_float(3.0)
不是一個精確的操作。 例如Decimal.from_float(0.3).= Decimal.from_float(0.1 + 0.2)
。 如果您的數據來自浮點數,您基本上無法比較它們是否完全相等,因為它們不精確,因此不應將它們用作鍵,也不應將它們與相等運算符(如==
或Decimal.eq?
.如果您只想將等效小數視為相等(解決上面的第 2 點,但不是第 3 點),您可以在將它們放入 map 之前對其進行歸一化:
Decimal.normalize(Decimal.new("3")) == Decimal.normalize(Decimal.new("3.0"))
但是,如果您從其他數據生成小數,這是一個危險的操作,因為不同的Decimal
結構被認為是等價的。 例如,考慮以下 map:
map = %{Decimal.new("3") => "three", Decimal.new("3.0") => "three point zero"}
如果我們規范化鍵,我們將失去一個唯一值並得到違反直覺的響應:
Map.new(map, fn {k, v} -> {Decimal.normalize(k), v} end)
%{#Decimal<3> => "three point zero"}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.