Credence.Pattern.NoEnumAtMidpointAccess
(credence v0.4.4)
Copy Markdown
Performance rule: Flags Enum.at/2 with a midpoint index inside
non-recursive functions.
Elixir lists are linked lists. Enum.at/2 is an O(n) operation. When the
index is derived from midpoint arithmetic (e.g. div(low + high, 2)), the
code almost certainly wants O(1) random access. Converting the list to a
tuple with List.to_tuple/1 and using elem/2 achieves this.
Auto-fixable
The fix inserts a List.to_tuple/1 call at the top of the enclosing
function body and replaces every flagged Enum.at/2 call with elem/2.
Pattern: direct call with midpoint variable
# Before
def find(list, low, high) do
mid = low + div(high - low, 2)
Enum.at(list, mid)
end
# After
def find(list, low, high) do
list_tuple = List.to_tuple(list)
mid = low + div(high - low, 2)
elem(list_tuple, mid)
endPattern: piped call
# Before
def find(list, low, high) do
mid = div(low + high, 2)
list |> Enum.at(mid)
end
# After
def find(list, low, high) do
list_tuple = List.to_tuple(list)
mid = div(low + high, 2)
elem(list_tuple, mid)
endPattern: inline midpoint expression
# Before
def find(list, low, high) do
Enum.at(list, div(low + high, 2))
end
# After
def find(list, low, high) do
list_tuple = List.to_tuple(list)
elem(list_tuple, div(low + high, 2))
endPattern: multiple lists
# Before
def compare(keys, values, low, high) do
mid = low + div(high - low, 2)
k = Enum.at(keys, mid)
v = Enum.at(values, mid)
{k, v}
end
# After
def compare(keys, values, low, high) do
keys_tuple = List.to_tuple(keys)
values_tuple = List.to_tuple(values)
mid = low + div(high - low, 2)
k = elem(keys_tuple, mid)
v = elem(values_tuple, mid)
{k, v}
endPattern: inside anonymous function (e.g. Enum.reduce_while)
# Before
def search(list, target) do
Enum.reduce_while(0..100, {0, length(list) - 1}, fn _, {low, high} ->
mid = low + div(high - low, 2)
mid_val = Enum.at(list, mid)
...
end)
end
# After — conversion at top of enclosing def, NOT inside the fn
def search(list, target) do
list_tuple = List.to_tuple(list)
Enum.reduce_while(0..100, {0, length(list) - 1}, fn _, {low, high} ->
mid = low + div(high - low, 2)
mid_val = elem(list_tuple, mid)
...
end)
endSee also Credence.Pattern.NoEnumAtBinarySearch which catches the same
anti-pattern in recursive functions (not auto-fixable).