{ }codelper

学习 › Python

数据 · 13

修改列表

判断是否包含、移除、反转、交换、用同一个值填充、复制。

列表建好之后,还可以移除值、改变顺序。
下面的例子先把一组卡片复制一份,询问某个数在不在其中,在就把它移除,再移除最前面的卡片,反转列表,交换最前面的两张。
最后输出修改后的列表、事先复制的原列表,以及一个用 0 填充的新列表。

n = int(input())cards = list(map(int, input().split()))x = int(input())original = cards.copy()    # 복사해 둬요if x in cards:    print("yes")    cards.remove(x)        # 값으로 빼기: 처음 나오는 하나만else:    print("no")cards.pop(0)               # 자리로 빼기: 0번cards.reverse()cards[0], cards[1] = cards[1], cards[0]  # 자리 바꾸기blank = [0] * len(cards)   # 0으로 채운 새 목록print(*cards)print(*original)print(*blank)
stdin
5
30 10 40 10 50
10
stdout
yes
10 50 40
30 10 40 10 50
0 0 0

要点

  • 判断是否包含的回答是真或假。
    不用循环逐个比较,一行就能写出来(C 要用循环查找)。
  • 移除有两种。
    按位置移除去掉第 i 格,按值移除只去掉这个值第一次出现的那一格。
    无论哪种,后面的值都会向前挪一格。
    要移除的值不存在时会怎样,各语言不同。
  • 反转不会生成新列表,而是把这个列表本身的顺序倒过来。
  • 交换:交换两格的值时,如果先写 a[0] = a[1],a[0] 原来的值就没了。
    要先把其中一个放进临时变量,或者使用一次交换两者的写法。
  • 用同一个值填充:只给出大小和初始值来建列表,比如含有 n 个 0 的列表。
  • 复制:第 08 课说过,b = a 只是给同一个列表再起一个名字。
    想保留原列表,要在修改之前另外复制一份。
    在 Python、JavaScript、Java、C#、Craykon 中,这种复制只复制一层:列表里装着列表的网格(第 12 课)复制之后各行仍然是共用的,要把每一行分别复制。

各语言的写法

Pythonx in a。
按值 a.remove(x)(不存在则 ValueError),按位置 a.pop(i) 或 del a[i]。
a.reverse()。
a[0], a[1] = a[1], a[0]。
[0] * n。
复制用 a.copy()
JavaScripta.includes(x)。
按位置 a.splice(i, 1)。
没有按值移除的函数,要先用 a.indexOf(x) 找到位置再 splice。
a.reverse()。
[a[0], a[1]] = [a[1], a[0]]。
new Array(n).fill(0)。
复制用 [...a]
TypeScript和 JavaScript 一样
Java在 List<Integer> 上用 a.contains(x)。
按位置 a.remove(i),按值 a.remove(Integer.valueOf(x))(不存在则返回 false)。
Collections.reverse(a)、Collections.swap(a, 0, 1)。
new ArrayList<>(Collections.nCopies(n, 0))。
复制用 new ArrayList<>(a)。
数组 new int[n] 一开始就全是 0
C#在 List<int> 上用 a.Contains(x)。
按值 a.Remove(x)(不存在则返回 false),按位置 a.RemoveAt(i)。
a.Reverse()。
(a[0], a[1]) = (a[1], a[0])。
Enumerable.Repeat(0, n).ToList()。
复制用 new List<int>(a)
C++std::find(a.begin(), a.end(), x) 不等于 a.end() 就表示存在。
找到的位置用 a.erase(it) 移除,第 i 格用 a.erase(a.begin() + i)。
std::reverse(a.begin(), a.end())、std::swap(a[0], a[1])。
std::vector<int> b(n, 0)。
b = a 本身就是复制
C数组大小固定,也没有这些函数。
用循环查找;移除时把后面的值逐个向前挪一格,再自己把个数减一。
反转是从两端向中间交换,交换用临时变量 t。
复制用 memcpy(b, a, n * sizeof a[0]) — b = a 是编译错误
Craykona.包含(x)。
按值 a.移除(x)(不存在则返回 false),按位置 a.按位置移除(i)。
a.反转()。
交换用临时变量。
新列表(n, 0)。
复制用 a.子列表(0, a.数量)

常见错误

一边遍历列表一边从中移除Python · JavaScript · TypeScript · Java · C#

a = [1, 1, 2, 3]
for v in a:
    if v == 1:
        a.remove(v)
print(a)

→ Python 输出 [1, 2, 3] — 后一个值挪进了被移除的那一格,所以第二个 1 被跳过了。JavaScript 的 for...of 结果相同。Java 以 ConcurrentModificationException 停止,C# 以 InvalidOperationException 停止。只有 Craykon 的 遍历 是照着开始时拍下的列表快照来走的,所以两个 1 都被移除,得到 2 3

只把要保留的值收集成新列表,或者用专门的函数:Python a = [v for v in a if v != 1],JavaScript a = a.filter(v => v !== 1),Java a.removeIf(v -> v == 1),C# a.RemoveAll(v => v == 1)。

想移除值 1,结果第 1 格被移除了Java

List<Integer> a = new ArrayList<>(List.of(1, 2, 3));
a.remove(1);
System.out.println(a);

→ [1, 3] — 传入 int 时是按位置移除。第 1 格的 2 不见了

要按值移除,写 a.remove(Integer.valueOf(1)) → [2, 3]。
对 [10, 20, 30] 写 a.remove(10),会去找第 10 格,以 IndexOutOfBoundsException 停止。

移除不存在的值,结果最后一个值被移除了JavaScript · TypeScript

const a = [1, 2, 3];
a.splice(a.indexOf(9), 1);
console.log(a);

→ [ 1, 2 ] — 值不存在时 indexOf 给出 -1,而 splice(-1, 1) 移除的是最后一格

先确认是否存在:const i = a.indexOf(9); if (i !== -1) a.splice(i, 1);

各语言的差异
反转是修改列表本身的命令,所以有的语言不能把它的结果拿来用。
Python 中写 b = a.reverse(),b 是 None。
需要一个反转后的新列表时,Python 用 a[::-1],JavaScript 用先复制再反转的 [...a].reverse()。
另外,C 中的 int *b = a; 不是复制,而是指向同一个数组的另一个名字:执行 b[0] = 9 后,a[0] 也变成 9。

练习题