Elixir MapSet与Range #

一、MapSet基础 #

1.1 MapSet定义 #

MapSet是无序、唯一元素的集合:

elixir
iex(1)> MapSet.new([1, 2, 3, 4, 5])
MapSet.new([1, 2, 3, 4, 5])

iex(2)> MapSet.new([1, 2, 2, 3, 3, 3])
MapSet.new([1, 2, 3])

iex(3)> MapSet.new()
MapSet.new([])

1.2 特性 #

  • 元素唯一
  • 无序存储
  • 快速成员检查
  • 元素可以是任意类型
elixir
iex(1)> set = MapSet.new([1, 2, 3])
MapSet.new([1, 2, 3])

iex(2)> MapSet.size(set)
3

iex(3)> MapSet.member?(set, 2)
true

iex(4)> MapSet.member?(set, 5)
false

二、MapSet操作 #

2.1 添加元素 #

elixir
iex(1)> set = MapSet.new([1, 2, 3])
MapSet.new([1, 2, 3])

iex(2)> MapSet.put(set, 4)
MapSet.new([1, 2, 3, 4])

iex(3)> MapSet.put(set, 2)
MapSet.new([1, 2, 3])

2.2 删除元素 #

elixir
iex(1)> set = MapSet.new([1, 2, 3])
MapSet.new([1, 2, 3])

iex(2)> MapSet.delete(set, 2)
MapSet.new([1, 3])

2.3 成员检查 #

elixir
iex(1)> set = MapSet.new([1, 2, 3])
MapSet.new([1, 2, 3])

iex(2)> MapSet.member?(set, 2)
true

iex(3)> MapSet.member?(set, 5)
false

iex(4)> 2 in set
true

2.4 集合运算 #

并集 #

elixir
iex(1)> a = MapSet.new([1, 2, 3])
MapSet.new([1, 2, 3])

iex(2)> b = MapSet.new([3, 4, 5])
MapSet.new([3, 4, 5])

iex(3)> MapSet.union(a, b)
MapSet.new([1, 2, 3, 4, 5])

交集 #

elixir
iex(1)> MapSet.intersection(a, b)
MapSet.new([3])

差集 #

elixir
iex(1)> MapSet.difference(a, b)
MapSet.new([1, 2])

iex(2)> MapSet.difference(b, a)
MapSet.new([4, 5])

对称差集 #

elixir
iex(1)> MapSet.symmetric_difference(a, b)
MapSet.new([1, 2, 4, 5])

2.5 子集判断 #

elixir
iex(1)> a = MapSet.new([1, 2])
MapSet.new([1, 2])

iex(2)> b = MapSet.new([1, 2, 3])
MapSet.new([1, 2, 3])

iex(3)> MapSet.subset?(a, b)
true

iex(4)> MapSet.subset?(b, a)
false

iex(5)> MapSet.equal?(a, b)
false

2.6 相等判断 #

elixir
iex(1)> a = MapSet.new([1, 2, 3])
MapSet.new([1, 2, 3])

iex(2)> b = MapSet.new([3, 2, 1])
MapSet.new([1, 2, 3])

iex(3)> MapSet.equal?(a, b)
true

2.7 转换 #

elixir
iex(1)> set = MapSet.new([1, 2, 3])
MapSet.new([1, 2, 3])

iex(2)> MapSet.to_list(set)
[1, 2, 3]

iex(3)> Enum.to_list(set)
[1, 2, 3]

三、MapSet遍历 #

3.1 使用Enum #

elixir
iex(1)> set = MapSet.new([1, 2, 3, 4, 5])
MapSet.new([1, 2, 3, 4, 5])

iex(2)> Enum.map(set, fn x -> x * 2 end)
[2, 4, 6, 8, 10]

iex(3)> Enum.filter(set, fn x -> x > 2 end)
[3, 4, 5]

iex(4)> Enum.reduce(set, 0, fn x, acc -> acc + x end)
15

3.2 使用推导式 #

elixir
iex(1)> set = MapSet.new([1, 2, 3, 4, 5])
MapSet.new([1, 2, 3, 4, 5])

iex(2)> for x <- set, x > 2, do: x * 2
[6, 8, 10]

四、MapSet使用场景 #

4.1 去重 #

elixir
iex(1)> list = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
[1, 2, 2, 3, 3, 3, 4, 4, 4, 4]

iex(2)> list |> MapSet.new() |> MapSet.to_list()
[1, 2, 3, 4]

iex(3)> Enum.uniq(list)
[1, 2, 3, 4]

4.2 成员检查 #

elixir
defmodule AccessControl do
  @admin_roles MapSet.new(["super_admin", "admin", "moderator"])

  def admin?(role) do
    MapSet.member?(@admin_roles, role)
  end
end

4.3 标签系统 #

elixir
defmodule TagSystem do
  def add_tags(post, new_tags) do
    current_tags = MapSet.new(post.tags)
    new_tags_set = MapSet.new(new_tags)
    updated_tags = MapSet.union(current_tags, new_tags_set)
    %{post | tags: MapSet.to_list(updated_tags)}
  end

  def remove_tags(post, tags_to_remove) do
    current_tags = MapSet.new(post.tags)
    remove_set = MapSet.new(tags_to_remove)
    updated_tags = MapSet.difference(current_tags, remove_set)
    %{post | tags: MapSet.to_list(updated_tags)}
  end

  def common_tags(post1, post2) do
    tags1 = MapSet.new(post1.tags)
    tags2 = MapSet.new(post2.tags)
    MapSet.intersection(tags1, tags2)
    |> MapSet.to_list()
  end
end

五、Range基础 #

5.1 Range定义 #

Range表示一个范围的整数:

elixir
iex(1)> 1..5
1..5

iex(2)> 1..10//2
1..10//2

iex(3)> 10..1
10..1

5.2 特性 #

  • 包含起始和结束值
  • 可以递增或递减
  • 支持步长
elixir
iex(1)> range = 1..5
1..5

iex(2)> Enum.to_list(range)
[1, 2, 3, 4, 5]

iex(3)> Enum.to_list(5..1)
[5, 4, 3, 2, 1]

iex(4)> Enum.to_list(1..10//2)
[1, 3, 5, 7, 9]

5.3 成员检查 #

elixir
iex(1)> 3 in 1..5
true

iex(2)> 6 in 1..5
false

iex(3)> 5 in 1..5
true

六、Range操作 #

6.1 遍历 #

elixir
iex(1)> Enum.map(1..5, fn x -> x * 2 end)
[2, 4, 6, 8, 10]

iex(2)> Enum.each(1..3, fn x -> IO.puts(x) end)
1
2
3
:ok

6.2 范围信息 #

elixir
iex(1)> range = 1..10
1..10

iex(2)> Range.size(range)
10

iex(3)> Range.new(1, 5)
1..5

iex(4)> Range.disjoint?(1..5, 6..10)
true

iex(5)> Range.disjoint?(1..5, 5..10)
false

6.3 步长范围 #

elixir
iex(1)> 1..10//2
1..10//2

iex(2)> Enum.to_list(1..10//2)
[1, 3, 5, 7, 9]

iex(3)> Enum.to_list(10..1//-1)
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

iex(4)> Enum.to_list(1..10//3)
[1, 4, 7, 10]

6.4 转换 #

elixir
iex(1)> Enum.to_list(1..5)
[1, 2, 3, 4, 5]

iex(2)> Enum.into(1..3, [])
[1, 2, 3]

七、Range模式匹配 #

7.1 匹配范围 #

elixir
iex(1)> first..last = 1..10
1..10

iex(2)> first
1

iex(3)> last
10

iex(4)> first..last//step = 1..10//2
1..10//2

iex(5)> step
2

7.2 在case中使用 #

elixir
iex(1)> case 5 do
...>   n when n in 1..10 -> "small"
...>   n when n in 11..100 -> "medium"
...>   _ -> "large"
...> end
"small"

八、Range使用场景 #

8.1 循环替代 #

elixir
for i <- 1..10 do
  IO.puts(i)
end

8.2 分页 #

elixir
defmodule Pagination do
  def page_range(current, total, window \\ 2) do
    start = max(1, current - window)
    finish = min(total, current + window)
    start..finish
  end
end

Pagination.page_range(5, 10)

8.3 字符范围 #

elixir
iex(1)> ?a..?z
97..122

iex(2)> for code <- ?a..?z, do: <<code>>
["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p",
 "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]

8.4 日期范围 #

elixir
defmodule DateRange do
  def days_in_range(start_date, end_date) do
    Date.diff(end_date, start_date) + 1
  end

  def date_range(start_date, end_date) do
    days = Date.diff(end_date, start_date)
    for offset <- 0..days do
      Date.add(start_date, offset)
    end
  end
end

九、性能考虑 #

9.1 MapSet vs List #

操作 MapSet List
成员检查 O(log n) O(n)
添加 O(log n) O(1) 头部
删除 O(log n) O(n)

9.2 Range vs List #

Range是惰性的,不会立即生成所有元素:

elixir
iex(1)> 1..1_000_000
1..1000000

iex(2)> Enum.to_list(1..1_000_000)

十、总结 #

MapSet #

操作 示例
创建 MapSet.new([1, 2, 3])
添加 MapSet.put(set, 4)
删除 MapSet.delete(set, 2)
成员检查 MapSet.member?(set, 2)
并集 MapSet.union(a, b)
交集 MapSet.intersection(a, b)

Range #

操作 示例
创建 1..10
步长 1..10//2
成员检查 5 in 1..10
大小 Range.size(1..10)
遍历 Enum.map(1..5, fn x -> x end)

准备好学习控制流了吗?让我们进入下一章。

最后更新:2026-03-27