Skip to content

Commit 1766731

Browse files
authored
Improvements to Enum.slide/3 (#11361)
- Support negative insertion indices - Give a clear RuntimeError, rather than a baffling CondClauseError, when you ask for an insertion point that matches the last element of your range
1 parent b65b8e2 commit 1766731

File tree

2 files changed

+41
-4
lines changed

2 files changed

+41
-4
lines changed

lib/elixir/lib/enum.ex

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2573,6 +2573,10 @@ defmodule Enum do
25732573
iex> Enum.slide([:a, :b, :c, :d, :e, :f, :g], -4..-2, 1)
25742574
[:a, :d, :e, :f, :b, :c, :g]
25752575
2576+
# Insert at negative indices (counting from the end)
2577+
iex> Enum.slide([:a, :b, :c, :d, :e, :f, :g], 3, -1)
2578+
[:a, :b, :c, :e, :f, :g, :d]
2579+
25762580
"""
25772581
def slide(enumerable, range_or_single_index, insertion_index)
25782582

@@ -2587,14 +2591,19 @@ defmodule Enum do
25872591
end
25882592

25892593
# Normalize negative input ranges like Enum.slice/2
2590-
def slide(enumerable, first..last, insertion_index) when first < 0 or last < 0 do
2594+
def slide(enumerable, first..last, insertion_index)
2595+
when first < 0 or last < 0 or insertion_index < 0 do
25912596
count = Enum.count(enumerable)
25922597
normalized_first = if first >= 0, do: first, else: first + count
25932598
normalized_last = if last >= 0, do: last, else: last + count
25942599

2595-
if normalized_first >= 0 and normalized_first < count and normalized_first != insertion_index do
2600+
normalized_insertion_index =
2601+
if insertion_index >= 0, do: insertion_index, else: insertion_index + count
2602+
2603+
if normalized_first >= 0 and normalized_first < count and
2604+
normalized_first != normalized_insertion_index do
25962605
normalized_range = normalized_first..normalized_last//1
2597-
slide(enumerable, normalized_range, insertion_index)
2606+
slide(enumerable, normalized_range, normalized_insertion_index)
25982607
else
25992608
Enum.to_list(enumerable)
26002609
end
@@ -2605,7 +2614,7 @@ defmodule Enum do
26052614
end
26062615

26072616
def slide(_, first..last, insertion_index)
2608-
when insertion_index > first and insertion_index < last do
2617+
when insertion_index > first and insertion_index <= last do
26092618
raise "Insertion index for slide must be outside the range being moved " <>
26102619
"(tried to insert #{first}..#{last} at #{insertion_index})"
26112620
end

lib/elixir/test/elixir/enum_test.exs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,7 @@ defmodule EnumTest do
823823
end
824824

825825
assert Enum.slide([:a, :b, :c, :d, :e, :f], 3..3, 2) == [:a, :b, :d, :c, :e, :f]
826+
assert Enum.slide([:a, :b, :c, :d, :e, :f], 3, 3) == [:a, :b, :c, :d, :e, :f]
826827
end
827828

828829
test "on a subsection of a list reorders the range correctly" do
@@ -946,6 +947,33 @@ defmodule EnumTest do
946947
assert slide.(list) == slide.(range)
947948
end
948949
end
950+
951+
test "inserts at negative indices" do
952+
for zero_to_5 <- [0..5, Enum.to_list(0..5)] do
953+
assert Enum.slide(zero_to_5, 0, -1) == [1, 2, 3, 4, 5, 0]
954+
assert Enum.slide(zero_to_5, 1, -1) == [0, 2, 3, 4, 5, 1]
955+
assert Enum.slide(zero_to_5, 1..2, -2) == [0, 3, 4, 1, 2, 5]
956+
assert Enum.slide(zero_to_5, -5..-4//1, -2) == [0, 3, 4, 1, 2, 5]
957+
end
958+
959+
assert Enum.slide([:a, :b, :c, :d, :e, :f], -5..-3//1, -2) ==
960+
Enum.slide([:a, :b, :c, :d, :e, :f], 1..3, 4)
961+
end
962+
963+
test "raises when insertion index would fall inside the range" do
964+
for zero_to_5 <- [0..5, Enum.to_list(0..5)] do
965+
assert_raise RuntimeError, fn ->
966+
Enum.slide(zero_to_5, 2..3, -3)
967+
end
968+
end
969+
970+
for zero_to_10 <- [0..10, Enum.to_list(0..10)],
971+
insertion_idx <- 3..5 do
972+
assert_raise RuntimeError, fn ->
973+
assert Enum.slide(zero_to_10, 2..5, insertion_idx)
974+
end
975+
end
976+
end
949977
end
950978

951979
test "scan/2" do

0 commit comments

Comments
 (0)