summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Bennett <potatosaladx@gmail.com>2018-04-30 08:11:37 -0500
committerMichał Muskała <michal@muskala.eu>2018-04-30 15:11:37 +0200
commit2ba20b6afada2359d08dcf5b516775f4bb26fbc8 (patch)
tree8d3e18fb1670dcb6a0908c300d27f4ba07820ce4
parent55543b8826beab9c5fac0011be6fa22aa3854e03 (diff)
downloadelixir-2ba20b6afada2359d08dcf5b516775f4bb26fbc8.tar.gz
Remove throw/catch pattern for Keyword.delete/2 and friends (#7622)
This prevents stacktrace corruption. Additionally short-circuit with `lists:keymember` for cases where the key to delete is not in the list. It seems to be a relatively common case and adds comparatively little overhead. Benchmarks: https://gist.github.com/potatosalad/f4fea8490bea09eb000a3a346a52cb6e
-rw-r--r--lib/elixir/lib/keyword.ex64
1 files changed, 41 insertions, 23 deletions
diff --git a/lib/elixir/lib/keyword.ex b/lib/elixir/lib/keyword.ex
index 13d14ffba..47e9c3390 100644
--- a/lib/elixir/lib/keyword.ex
+++ b/lib/elixir/lib/keyword.ex
@@ -463,19 +463,23 @@ defmodule Keyword do
"""
@spec delete(t, key, value) :: t
def delete(keywords, key, value) when is_list(keywords) and is_atom(key) do
- delete_key_value(keywords, key, value, _deleted? = false)
- catch
- :not_deleted -> keywords
+ case :lists.keymember(key, 1, keywords) do
+ true -> delete_key_value(keywords, key, value)
+ _ -> keywords
+ end
end
- defp delete_key_value([{key, value} | rest], key, value, _deleted?),
- do: delete_key_value(rest, key, value, true)
+ defp delete_key_value([{key, value} | tail], key, value) do
+ delete_key_value(tail, key, value)
+ end
- defp delete_key_value([{_, _} = pair | rest], key, value, deleted?),
- do: [pair | delete_key_value(rest, key, value, deleted?)]
+ defp delete_key_value([{_, _} = pair | tail], key, value) do
+ [pair | delete_key_value(tail, key, value)]
+ end
- defp delete_key_value([], _key, _value, _deleted? = true), do: []
- defp delete_key_value([], _key, _value, _deleted? = false), do: throw(:not_deleted)
+ defp delete_key_value([], _key, _value) do
+ []
+ end
@doc """
Deletes the entries in the keyword list for a specific `key`.
@@ -497,18 +501,23 @@ defmodule Keyword do
@spec delete(t, key) :: t
@compile {:inline, delete: 2}
def delete(keywords, key) when is_list(keywords) and is_atom(key) do
- delete_key(keywords, key, _deleted? = false)
- catch
- :not_deleted -> keywords
+ case :lists.keymember(key, 1, keywords) do
+ true -> delete_key(keywords, key)
+ _ -> keywords
+ end
end
- defp delete_key([{key, _} | rest], key, _deleted?), do: delete_key(rest, key, true)
+ defp delete_key([{key, _} | tail], key) do
+ delete_key(tail, key)
+ end
- defp delete_key([{_, _} = pair | rest], key, deleted?),
- do: [pair | delete_key(rest, key, deleted?)]
+ defp delete_key([{_, _} = pair | tail], key) do
+ [pair | delete_key(tail, key)]
+ end
- defp delete_key([], _key, _deleted? = true), do: []
- defp delete_key([], _key, _deleted? = false), do: throw(:not_deleted)
+ defp delete_key([], _key) do
+ []
+ end
@doc """
Deletes the first entry in the keyword list for a specific `key`.
@@ -525,14 +534,23 @@ defmodule Keyword do
"""
@spec delete_first(t, key) :: t
def delete_first(keywords, key) when is_list(keywords) and is_atom(key) do
- delete_first_key(keywords, key)
- catch
- :not_deleted -> keywords
+ case :lists.keymember(key, 1, keywords) do
+ true -> delete_first_key(keywords, key)
+ _ -> keywords
+ end
+ end
+
+ defp delete_first_key([{key, _} | tail], key) do
+ tail
end
- defp delete_first_key([{key, _} | rest], key), do: rest
- defp delete_first_key([{_, _} = pair | rest], key), do: [pair | delete_first_key(rest, key)]
- defp delete_first_key([], _key), do: throw(:not_deleted)
+ defp delete_first_key([{_, _} = pair | tail], key) do
+ [pair | delete_first_key(tail, key)]
+ end
+
+ defp delete_first_key([], _key) do
+ []
+ end
@doc """
Puts the given `value` under `key`.