Python AtCoder 入門 第6講 タプルと内包表記

>100 Views

September 19, 26

スライド概要

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

ダウンロード

関連スライド

各ページのテキスト
1.

Python AtCoder入門 第6講 タプルと内包表記 第5講でリストを一通り扱えるようになりました。 この講では、リストの兄弟にあたる タプル と、リストを短く作る 内包表記 を学びます。 1

2.

今回のテーマ タプルと内包表記は、どちらも データをまとめて扱うための道具 です。 この先の講で繰り返し必要になる前提知識です。 2

3.

この講のゴール この講では、次を身につけます。 タプルの作り方 アンパック タプルが必要になる場面 リスト内包表記 入力処理での内包表記 ジェネレータ式 3

4.

コードファイル名の方針 この講でも、コード例ごとにファイル名を付けます。 tuple_basic.py points_set.py list_comprehension.py grid_init.py generator_sum.py answer_6_1.py 4

5.

6-1 タプルの基本 タプルは、丸括弧 ( ) で作ります。 t = (3, 7) print(t) print(t[0]) print(t[1]) リストと同じように、インデックスでアクセスできます。 5

6.

tuple_basic_access.py t = (3, 7) print(t) print(t[0]) print(t[1]) 出力: (3, 7) 3 7 6

7.

リストと同じように使える部分 タプルでも、次の操作はリストと同じです。 インデックスでアクセス len() で長さを取る for で順に取り出す t = (3, 7) print(len(t)) 7

8.

括弧は省略できる 実は、タプルの括弧は省略できます。 t = 3, 7 これもタプルです。 カンマがあればタプルになる と覚えてください。 8

9.

one_tuple.py 要素が1つのタプルを作るときは、末尾のカンマが必要です。 a = (3) b = (3,) print(type(a)) print(type(b)) 出力: <class 'int'> <class 'tuple'> 9

10.

tuple変換 tuple() を使うと、リストなどをタプルに変換できます。 A = [3, 1, 4] t = tuple(A) print(t) 出力: (3, 1, 4) 10

11.

list変換 逆に、 list() を使えばタプルからリストに戻せます。 t = (3, 1, 4) A = list(t) print(A) 出力: [3, 1, 4] 11

12.

入力をタプルで受け取る 入力をそのままタプルにすることもできます。 A = tuple(map(int, input().split())) 第1講で覚えた、 list(map(int, input().split())) の list を tuple に変えただけです。 12

13.

listとtupleの使い分け 判断は単純です。 あとで書き換えるなら list 書き換えず、1件分のデータとして扱うなら tuple 通常の数列は list 。 座標やレコードは tuple が便利です。 13

14.

N行のデータをタプルで保存する # Store each line as one immutable record N = int(input()) data = [tuple(map(int, input().split())) for _ in range(N)] N行ぶんのデータが、 タプルのリスト になります。 14

15.

タプルは変更できない リストは変更できます。 A = [3, 7] A[0] = 100 しかし、タプルは変更できません。 t = (3, 7) t[0] = 100 これは TypeError になります。 15

16.

immutable.py A = [3, 7] A[0] = 100 print(A) t = (3, 7) t[0] = 100 タプルは作ったあとで中身を書き換えられません。 この性質を イミュータブル と呼びます。 16

17.

アンパック代入 タプルの中身は、変数を並べて一度に取り出せます。 t = (3, 7) a, b = t print(a) print(b) これを アンパック と言います。 17

18.

unpack.py t = (3, 7) a, b = t print(a) print(b) 出力: 3 7 左辺の変数の個数と、タプルの要素数が一致している必要があります。 18

19.

変数の値を交換する アンパックを使うと、2つの変数の値を1行で交換できます。 a = 1 b = 2 a, b = b, a print(a, b) 出力: 2 1 19

20.

実は最初から使っていた 第1講で使ったこの書き方もアンパックです。 A, B = map(int, input().split()) 右辺から2つの値が出てきて、 A と B に順番に入ります。 20

21.

enumerateもアンパック 第4講のこの書き方も同じです。 for i, x in enumerate(A): print(i, x) が、インデックスと中身の組を返します。 それを i と x にアンパックしています。 enumerate(A) 21

22.

tuple_basic.py # Create a tuple t = (3, 7) print(t[0], t[1], len(t)) # Parentheses can be omitted u = 10, 20 print(u) # Unpacking a, b = u print(a, b) # Swapping two values a, b = b, a print(a, b) 22

23.

tuple_basic.py の出力 3 7 2 (10, 20) 10 20 20 10 タプル、括弧の省略、アンパック、交換を確認できます。 23

24.

関数から複数の値を返す タプルのおかげで、関数から複数の値をまとめて返せます。 def divide(a, b): return a // b, a % b return の右側にカンマがあるので、タプルを返します。 24

25.

return_tuple.py # Return the quotient and the remainder at once def divide(a, b): return a // b, a % b # The returned tuple is unpacked into two variables q, r = divide(17, 5) print(q, r) 出力: 3 2 25

26.

6-2 タプルが必要になる場面 リストがあるのに、なぜタプルが必要なのでしょうか。 大きな理由は2つです。 1. 辞書のキーや集合の要素にできる 2. 1件分のデータをひとまとめにできる 26

27.

① 集合の要素にできる 第10講で学ぶ set には、 中身が変わらないもの しか入れられません。 リストは変更できるので入れられません。 タプルは変更できないので入れられます。 27

28.

set_tuple.py s = set() s.add((1, 2)) s.add([1, 2]) # OK # TypeError タプルは集合に入れられます。 リストは集合に入れられません。 28

29.

座標の管理 この性質が効くのが、座標の管理です。 (x, y) のような座標をタプルにして、集合に入れます。 重複した座標を1つとして扱えます。 29

30.

points_set.py # Read the number of points N = int(input()) # Store each point as a tuple in a set points = set() for _ in range(N): x, y = map(int, input().split()) points.add((x, y)) # Duplicated points are counted only once print(len(points)) 30

31.

points_set.py の実行例 入力例: 5 1 2 3 4 1 2 5 6 3 4 出力例: 3 重複を除くと、3種類の点です。 31

32.

tupleで直接追加する アンパックを経由せず、直接タプルにして追加できます。 for _ in range(N): points.add(tuple(map(int, input().split()))) 読み取った1行を、そのまま1つの座標として集合に入れます。 32

33.

② 1件分のデータをまとめる 別々の値でも、1組で意味を持つデータがあります。 例: 名前と点数 日付と気温 x座標とy座標 このようなものは、タプルにまとめると扱いやすくなります。 33

34.

レコードとしてのタプル data = [("Sato", 80), ("Suzuki", 45), ("Takahashi", 72)] この形にしておけば、並べ替えても名前と点数がずれません。 第13講のソートで特に便利です。 34

35.

student_records.py # Read the number of students N = int(input()) # Store each student as a (name, score) tuple data = [] for _ in range(N): name, score = input().split() data.append((name, int(score))) # Unpack each tuple in the loop for name, score in data: if score >= 60: print(name, score) 35

36.

student_records.py の実行例 入力例: 3 Sato 80 Suzuki 45 Takahashi 72 出力例: Sato 80 Takahashi 72 点数だけ int() で変換しています。 36

37.

リストとタプルの比較 リスト タプル 中身の変更 できる できない 辞書のキー・集合の要素 使えない 使える 主な用途 同じ種類のデータの並び 1件分のデータの組 37

38.

使い分けの目安 競技プログラミングでは、だいたい次の使い分けです。 N個の数列 → リスト 座標 → タプル 名前と点数のような1件分のデータ → タプル あとで書き換えるならリストです。 38

39.

6-3 リスト内包表記 ここから話題が変わります。 リストを短く作るための書き方 を学びます。 39

40.

appendでリストを作る 第5講では、次のように書きました。 A = [] for i in range(5): A.append(i * i) これは、二乗のリストを作るコードです。 40

41.

内包表記で1行にする 同じことを1行で書けます。 A = [i * i for i in range(5)] これが リスト内包表記 です。 41

42.

内包表記の基本形 [ 式 for 変数 in 範囲 ] 読むときは、後ろから読むのがコツです。 range(5) の各 i について、i * i を並べたリスト です。 42

43.

list_comprehension_basic.py A = [i * i for i in range(5)] print(*A) 出力: 0 1 4 9 16 43

44.

条件付き内包表記 末尾に if を付けると、条件を満たすものだけを集められます。 [式 for 変数 in 範囲 if 条件] 条件で絞り込んでから、リストにします。 44

45.

even_list.py A = [3, 1, 4, 1, 5, 9, 2, 6] even = [x for x in A if x % 2 == 0] print(*even) 出力: 4 2 6 偶数だけを取り出しています。 45

46.

list_comprehension.py # Squares of 0 to 4 squares = [i * i for i in range(5)] print(*squares) # Extract even numbers A = [3, 1, 4, 1, 5, 9, 2, 6] even = [x for x in A if x % 2 == 0] print(*even) # Combine both doubled_even = [x * 2 for x in A if x % 2 == 0] print(*doubled_even) 46

47.

list_comprehension.py の出力 0 1 4 9 16 4 2 6 8 4 12 3つ目は、 偶数だけを2倍したリスト です。 47

48.

for文との対応 result = [] for x in A: if 条件: result.append(式) これは、内包表記でこう書けます。 result = [式 for x in A if 条件] 48

49.

comprehension_compare.py A = [3, 1, 4, 1, 5, 9, 2, 6] # Using a for loop result1 = [] for x in A: if x >= 4: result1.append(x * 10) # Using a list comprehension result2 = [x * 10 for x in A if x >= 4] print(*result1) print(*result2) 49

50.

comprehension_compare.py の出力 40 50 90 60 40 50 90 60 同じ結果です。 読みにくいと感じるうちは、 for 文で書いても構いません。 50

51.

6-4 内包表記と入力処理 内包表記が最も威力を発揮するのが、 入力処理 です。 特に、N行入力を受け取るときに便利です。 51

52.

N行の入力を1行で受け取る A = [int(input()) for _ in range(N)] これは、 N行読み取って整数のリストにする という意味です。 52

53.

for文で書くと A = [] for _ in range(N): A.append(int(input())) これと同じことを、内包表記なら1行で書けます。 A = [int(input()) for _ in range(N)] 53

54.

input_lines_comprehension.py # Read N and then N integers, one per line N = int(input()) A = [int(input()) for _ in range(N)] print(sum(A), max(A)) 入力例: 4 10 25 18 7 出力例: 60 25 54

55.

複数値のN行入力 N行にわたって「名前 点数」のような値が並ぶ場合も同じです。 data = [input().split() for _ in range(N)] 各行がリストになり、それがN行ぶん集まります。 55

56.

2次元リストの初期化 もう1つの重要な用途が、 2次元リスト の初期化です。 H行W列のマス目を、すべて0で埋めたい場合を考えます。 56

57.

正しい2次元リスト grid = [[0] * W for _ in range(H)] これは、 長さWのリストを、H個作る という意味です。 57

58.

危険な書き方 これはやってはいけません。 grid = [[0] * W] * H 一見同じに見えますが、壊れます。 H個の行が、すべて同じ1つのリストを指してしまいます。 58

59.

grid_init.py H = 3 W = 4 # Create an H x W grid filled with zeros grid = [[0] * W for _ in range(H)] # Modify one cell grid[1][2] = 9 # Print row by row for row in grid: print(*row) 59

60.

grid_init.py の出力 0 0 0 0 0 0 9 0 0 0 0 0 書き換えた1か所だけが変わっています。 60

61.

6-5 その他の内包表記 角括弧を波括弧に変えると、集合や辞書も作れます。 ここでは、形だけ知っておけば十分です。 集合と辞書は第10講で扱います。 61

62.

集合内包表記 A = [3, 1, 4, 1, 5] s = {x for x in A} print(s) 重複が自動的に消えます。 62

63.

辞書内包表記 A = [3, 1, 4, 1, 5] d = {x: x * x for x in A} print(d) キーと値を : で結びます。 63

64.

set_dict_comprehension.py A = [3, 1, 4, 1, 5] # Set comprehension removes duplicates s = {x for x in A} print(len(s)) # Dictionary comprehension maps each value to its square d = {x: x * x for x in A} print(d[4]) 出力: 4 16 64

65.

ジェネレータ式 のような関数に内包表記を渡すとき、 角括弧を省略できます。 sum() sum(x for x in A if x % 2 == 0) これをジェネレータ式と呼びます。 65

66.

リストを作る場合 A = [3, 1, 4, 1, 5, 9, 2, 6] print(sum([x for x in A if x % 2 == 0])) これは、偶数だけのリストを作ってから合計しています。 66

67.

リストを作らない場合 A = [3, 1, 4, 1, 5, 9, 2, 6] print(sum(x for x in A if x % 2 == 0)) 途中のリストを実際には作りません。 1つずつ値を生み出して足していきます。 67

68.

generator_sum.py # Read N and the list of N integers N = int(input()) A = list(map(int, input().split())) # Sum of even numbers print(sum(x for x in A if x % 2 == 0)) # Count of even numbers print(sum(1 for x in A if x % 2 == 0)) 68

69.

generator_sum.py の実行例 入力例: 6 3 1 4 1 5 6 出力例: 10 2 偶数の合計は 10 、個数は 2 です。 69

70.

sum(1 for ...) sum(1 for x in A if x % 2 == 0) これは、条件を満たすたびに 1 を足す書き方です。 つまり、 条件を満たす個数を数える ことになります。 70

71.

内包表記を使いすぎない 内包表記は強力ですが、詰め込みすぎると読みにくくなります。 result = [x * y for x in A for y in B if x % 2 == 0 and y > 3] このくらい複雑なら、 for 文に戻したほうが安全です。 71

72.

for文で書くと result = [] for x in A: for y in B: if x % 2 == 0 and y > 3: result.append(x * y) このほうが読みやすいなら、無理に内包表記にしなくてよいです。 72

73.

目安 内包表記は、読みやすくなるときに使います。 目安として、 forが2つ以上入るなら、for文に戻す ことをおすすめします。 短さよりも、5秒で意味がわかることを優先しましょう。 73

74.

章末まとめ:タプル タプルは (3, 7) で作る 括弧は省略できる カンマがあればタプル インデックス、 len 、 for はリストと同じ タプルは変更できない 74

75.

章末まとめ:アンパック タプルは、アンパックできます。 a, b = t 値の交換もできます。 a, b = b, a A, B = map(...) もアンパックです。 75

76.

章末まとめ:タプルの使い所 タプルは変更できないので、 辞書のキー 集合の要素 として使えます。 また、座標や1件分のデータをまとめるのに向いています。 76

77.

章末まとめ:内包表記 リスト内包表記は、 [式 for 変数 in 範囲] です。 条件で絞るなら、 [式 for 変数 in 範囲 if 条件] です。 77

78.

章末まとめ:入力処理 N行入力は、内包表記で短く書けます。 A = [int(input()) for _ in range(N)] 2次元リストは、次の形で作ります。 grid = [[0] * W for _ in range(H)] 78

79.

章末まとめ:ジェネレータ式 sum() などに渡すときは、角括弧を省略できます。 sum(x for x in A if 条件) 個数を数えるなら、 sum(1 for x in A if 条件) です。 79

80.

練習問題 6-1 二乗して出力 個の整数 A_1, A_2, ..., A_N が与えられます。 それぞれを2乗した値を、空白区切りで1行に出力してください。 N 80

81.

練習問題 6-1:入力と出力 入力: N A_1 A_2 ... A_N 入力例: 5 3 1 4 1 5 出力例: 9 1 16 1 25 81

82.

answer_6_1.py # Read N and the list of N integers N = int(input()) A = list(map(int, input().split())) # Square every element B = [x * x for x in A] print(*B) 内包表記で新しいリストを作ります。 82

83.

answer_6_1_direct.py # Read N and the list of N integers N = int(input()) A = list(map(int, input().split())) print(*[x * x for x in A]) リストを変数に入れず、直接出力することもできます。 83

84.

練習問題 6-2 合格者の名前 人の生徒の名前と点数が、1行に1人ずつ与えられます。 点数が60以上の生徒の名前を、入力された順に1行ずつ出力してください。 該当者がいない場合は、何も出力しません。 N 84

85.

練習問題 6-2:入力例 入力例: 4 Sato 80 Suzuki 45 Takahashi 72 Tanaka 60 出力例: Sato Takahashi Tanaka 60 ちょうども含みます。 85

86.

answer_6_2.py # Read the number of students N = int(input()) # Read each line and print the name if the score is high enough for _ in range(N): name, score = input().split() if int(score) >= 60: print(name) 点数だけ int() で変換します。 86

87.

answer_6_2_data.py # Read the number of students N = int(input()) # Store each student as a (name, score) tuple data = [input().split() for _ in range(N)] for name, score in data: if int(score) >= 60: print(name) いったんデータとして保存してから処理する書き方です。 87

88.

練習問題 6-3 偶数の合計と個数 個の整数 A_1, A_2, ..., A_N が与えられます。 このうち偶数であるものについて、 合計 と 個数 を、この順に空白区切りで1行に出力してください。 N 88

89.

練習問題 6-3:入力と出力 入力例: 6 3 1 4 1 5 6 出力例: 10 2 偶数が1つもない場合は、 0 0 と出力します。 89

90.

answer_6_3.py # Read N and the list of N integers N = int(input()) A = list(map(int, input().split())) # Sum and count of even numbers print(sum(x for x in A if x % 2 == 0), sum(1 for x in A if x % 2 == 0)) ジェネレータ式で合計と個数を求めます。 90

91.

answer_6_3_even_list.py # Read N and the list of N integers N = int(input()) A = list(map(int, input().split())) # Extract even numbers first even = [x for x in A if x % 2 == 0] print(sum(even), len(even)) 同じ条件を2回書くのが気になる場合はこちらが読みやすいです。 91

92.

第6講まとめ この講では、 タプルと内包表記 を学びました。 タプルは1件分のデータをまとめる道具。 内包表記はリストを短く作る道具です。 92

93.

次回予告 次の第7講では、 文字列 を扱います。 第5講のスライスや、この講の内包表記が、そのまま文字列にも使えることがわかります。 93