プログラミング演習・14章版 第10章 文字列・辞書・集合・内包表記
教材コード

文字列・辞書・集合・内包表記

データを扱うには,その形に合った容器を選ぶ必要があります.文章は文字列,項目名と値の対応は辞書,重複のない集まりは集合で表します.本章では取り出し方と加工の仕方を学ぶ.

文字列の位置,切り出し,メソッド

文字列を位置で取り出し,範囲で切り出す

文字列(string)は文字を順に並べた値です.先頭の添字(index)は0であり,スライス(slice)では開始位置を含み終了位置を含みません.

s[a:b] は半開区間 [a, b) に対応する位置の文字列を返します.長さ n の文字列では有効な正の添字は0から n − 1 までです.負の添字 -1 は最後の文字を指す.文字列は変更不可であり,strip などのメソッドは新しい文字列を返します.

文字列を0番から並ぶデータ列として考えます.s[1:4] は位置1,2,3を取り,位置4は取りません.端の余白を消したり,区切りで分けたりする操作は,新しいデータ列を作ります.

"Python" の添字. [1:4] は1番から3番まで
"Python" の添字.[1:4] は1番から3番まで
chapter-10_code_1.py
s = "Python"
print(s[0], s[-1])
print(s[1:4])
row = " a,b "
print(row.strip().split(","))
実行結果の例
P n
yth
['a', 'b']

1行目は文字列を用意します.2行目は先頭と末尾を選ぶ.3行目は添字1から3までを取り出します.4行目の文字列には両端に空白があります.5行目では strip() が空白を除き,split(",") がカンマで二つに分ける.

s[1:4] の4番の文字は含まれません.s[0] = "A" は文字列が変更不可のため失敗します.変更後の文字列が必要なら新しく組み立てる.

そのまま演習文字列の位置,切り出し,メソッド · そのまま演習

そのまま演習:"Python"[2:5] の結果を予想して実行します.

ヒントを見る

文字の添字は0から始まり、スライスの終点は含まれません。変更するスライスと、後ろの文字列分割の行を区別します。

演習の解説を見る

解説:添字2,3,4の "tho" です.終点5は含みません.

虫食い文字列の位置,切り出し,メソッド · 虫食い

虫食い演習:外側の空白を除いてからカンマで分割します.

chapter-10_code_3.py
print(" a,b ".___().split(","))
ヒントを見る

先に文字列全体の両端の空白を除き、その後でカンマにより分けます。空欄は引数なしで呼び出す文字列メソッドです。

演習の解説を見る

解説:空欄は strip です.split の前に置くと,先に余白を除ける.

添字と長さを照合する

文字列 "Python" の長さは6です.先頭から数える添字は0,1,2,3,4,5となります.したがって6番の文字を一つ取り出す指定は範囲外です.

chapter-10_code_4.py
s = "Python"
print(len(s))
print(s[len(s) - 1])
print(s[:2], s[2:])
実行結果の例
6
n
Py thon

1行目で文字列を作ります.2行目は文字数6を表示します.3行目は len(s)-1 が5なので末尾の n を出します.4行目の s[:2] は0番と1番,s[2:] は2番から末尾までです.二つをつなぐと元の文字列に戻る.

s[len(s)] は添字6を指定するため IndexError になります.最後の文字なら s[-1] または s[len(s)-1] と書きます.空文字列ではどちらも取り出せないので,入力が空の可能性がある場合は先に長さを確認します.

そのまま演習添字と長さを照合する · そのまま演習

そのまま演習:s[:3] と s[3:] の結果を予想します.

ヒントを見る

長さは文字数、最後の添字は長さから1を引いた数です。二つのスライスが重ならず全体を分ける位置も確認します。

演習の解説を見る

解説:前半は "Pyt",後半は "hon" です.境目の添字3は後半だけに入ります.

虫食い添字と長さを照合する · 虫食い

虫食い演習:末尾1文字を取り出す s[___] の空欄を補います.

ヒントを見る

最後の1文字は末尾側から数える添字で指定できます。先頭の0とは異なり、負の添字で末尾からの位置を表します。

演習の解説を見る

解説:空欄は -1 です.負の添字は末尾から数える.

正規表現で文字の形を調べる

正規表現(regular expression)は文字列の形を指定する記法です.re.fullmatch は文字列全体が指定した形に一致したときだけ結果を返します.re.sub は一致した部分を置き換える.

[A-Za-z0-9] は英字または数字1文字を表す文字クラスで,{3} は直前の形を3回繰り返します.fullmatch は入力全体を検査し,一致しなければ None を返します.bool で真偽値に変換できます.パターンには r"..." という raw 文字列を用いると,バックスラッシュをPythonの文字列処理で重ねて解釈されにくい.

「英字か数字を3個」という型紙を作り,入力した文字列を重ねてはみ出さないか調べる方法です.A1b は3文字なので合い,A1b4 は4文字なので合いません.

chapter-10_code_6.py
import re
kata = r"[A-Za-z0-9]{3}"
print(bool(re.fullmatch(kata, "A1b")))
print(bool(re.fullmatch(kata, "A1b4")))
print(re.sub(r"[0-9]+", "#", "A12-B3"))
実行結果の例
True
False
A#-B#

1行目は正規表現のモジュールを読みます.2行目は英数字3文字という形を保存します.3行目は全体が一致します.4行目は長さが違うので一致しません.5行目は一つ以上続く数字を # に置き換える.

そのまま演習正規表現で文字の形を調べる · そのまま演習

そのまま演習:"12A" と "12-" を fullmatch で調べます.

ヒントを見る

英字と数字を許すパターンでも、文字数の条件は残ります。追加した入力の文字種と長さを別々に検査します。

演習の解説を見る

解説:前者は英数字3文字で True,後者の - は文字クラスにないため False です.

虫食い正規表現で文字の形を調べる · 虫食い

虫食い演習:数字3文字だけの形を完成させる.

chapter-10_code_8.py
print(bool(re.fullmatch(r"[0-9]{___}", "123")))
ヒントを見る

波括弧の数は直前の数字パターンを繰り返す回数です。入力123の文字数と対応させます。

演習の解説を見る

解説:空欄は 3 です.[0-9] を3回繰り返します.

re.match は先頭だけを調べる場合があります.文字列全体の形式確認には re.fullmatch を用います.複雑な入力を正規表現だけで無理に処理せず,分割や型変換との組合せを考えます.

条件を一つずつ検証する

[0-9]{3} は「数字がちょうど3文字」という条件です.数字以外の文字が混ざる場合と長さが違う場合を別々に試すと,形の意味を確認しやすい.

chapter-10_code_9.py
import re
kata = r"[0-9]{3}"
for value in ["123", "12A", "1234", "012"]:
    print(value, bool(re.fullmatch(kata, value)))
実行結果の例
123 True
12A False
1234 False
012 True

1行目は標準ライブラリを読みます.2行目は数字3文字の形を作ります.3行目は4種類の入力を順に取り出します.4行目は全文一致の結果を真偽値に直します."012" は整数として読む前の文字列であり,先頭の0も1文字に数える.

r"[0-9]{3}" は文字列の形式を検査するもので,値が0から999の整数かどうかを調べる式ではありません.たとえば "012" は形式に合う.数としての範囲まで必要なら,形式を確認した後で int に変換して調べます.

そのまま演習条件を一つずつ検証する · そのまま演習

そのまま演習:"9" と "999" の一致を調べます.

ヒントを見る

同じパターンに、長さの違う二つの入力を順に渡します。一致結果は入力ごとに別の行へ表示します。

演習の解説を見る

解説:1文字の "9" は偽,3文字の "999" は真です.

虫食い条件を一つずつ検証する · 虫食い

虫食い演習:英字3文字に限定するため r"[A-Za-z]{___}" を完成させる.

ヒントを見る

大文字と小文字の英字を、指定した文字数だけ受け付けます。二つの入力の長さを数え、両方のパターンに同じ回数を書きます。

演習の解説を見る

解説:空欄は 3 です.数字は文字クラスに含まれません.

タプル,辞書,集合

辞書のキーと値の対応

次の2図は同じ構図です.図10.2を見て値を予想してから,図10.3で確認します.

辞書から値を読む:書き込む前
辞書から値を読む:書き込んだ後

タプル(tuple)は順序を持つ変更不可の値の組です.辞書(dictionary)はキー(key)と値の対応を保持します.集合(set)は重複を持たない要素の集まりです.

タプルは固定長の組として座標などを表せる.辞書はキーから値への対応を保存し,get はキーがない場合の既定値も指定できます.集合の & は共通部分です.用途によって,位置で取り出すリスト,キーで取り出す辞書,所属と重複を扱う集合を使い分ける.

タプルは書き換えない2点セット,辞書は名前から点数を探す名簿,集合は同じ名前を2回書かない参加者一覧です.欲しい情報を「何番目」で探すか,「名前」で探すか,「入っているか」で調べるかで容器を選ぶ.

chapter-10_code_11.py
point = (3, 4)
x, y = point
scores = {"A": 80, "B": 70}
print(x + y, scores["A"])
print(scores.get("C", 0))
print({1, 2, 3} & {2, 3, 4})
実行結果の例
7 80
0
{2, 3}

1行目は座標の組を作り,2行目は二つの値を別々の変数へ代入します.3行目は文字列のキーと点数の対応を作ります.4行目は座標の和とAの点数を出します.5行目は存在しないCに対して既定値0を返します.6行目は二つの集合の共通要素を求めます.集合の表示順は保証されません.

存在しない辞書のキーを scores["C"] と書くと KeyError になります.キーがないことを想定する場合は get や in で扱う.空集合は set() で作ります.{} は空の辞書です.

そのまま演習タプル,辞書,集合 · そのまま演習

そのまま演習:scores.get("B", 0) を表示します.

ヒントを見る

辞書に保存されているキーでは get の既定値は使われません。既存のタプルと集合の表示は残します。

演習の解説を見る

解説:Bは登録されているので70です.既定値0は使われません.

虫食いタプル,辞書,集合 · 虫食い

虫食い演習:共通要素だけを取り出す記号を補います.

chapter-10_code_13.py
print({1, 2, 3} ___ {2, 3, 4})
ヒントを見る

両方の集合に含まれる要素だけを残す演算です。合併して全要素を集める演算とは区別します。

演習の解説を見る

解説:空欄は & です.結果の要素は2と3です.

どの容器を選ぶか

同じ値から異なる問いを立てる.「Aの点数は何点か」には辞書,「登録された番号に重複があるか」には集合が適します.

chapter-10_code_14.py
scores = {"A": 80, "B": 70}
numbers = [1, 2, 1, 3]
print(scores.get("A", 0))
print(len(numbers), len(set(numbers)))
print(2 in set(numbers))
実行結果の例
80
4 3
True

1行目は名前と点数の対応を作ります.2行目は重複した番号を含む一覧です.3行目はAの点数80を取り出します.4行目は一覧4件に対して異なる値が3種類であることを示します.5行目は2が集合に含まれるので真を表示します.

set(numbers) は重複を除くが,元の並び順を保存するための容器ではありません.順番が大切なら元のリストを残す.また辞書はキーが一意であり,同じキーを二度書くと後の値で前の値を置き換える.

そのまま演習どの容器を選ぶか · そのまま演習

そのまま演習:numbers にもう一つ 2 を加え,二つの長さを比べます.

ヒントを見る

元リストの長さと、重複を除いた集合の長さは別です。所属の検査では個数ではなく、その数があるかを調べます。

演習の解説を見る

解説:リストの長さは5,集合にした後の長さは3です.すでにある2を加えても種類は増えません.

虫食いどの容器を選ぶか · 虫食い

虫食い演習:print(len(___(numbers))) で異なる値の種類数を出します.

ヒントを見る

同じ数が何回現れたかではなく、異なる数の種類数を求めます。重複を持たないデータ構造へ変えてから長さを調べます。

演習の解説を見る

解説:空欄は set です.重複を除いた集合の要素数を測る.

内包表記とコピー

内包表記(comprehension)は,繰り返しによる新しいリスト,辞書,集合の作成を簡潔に書く方法です.sorted は元のリストを変更せず,新しいリストを返します.

[f(x) for x in values if p(x)] は,条件 p(x) を満たす各要素に f を適用したリストを作ります.辞書内包表記は {key: value for ...},集合内包表記は {value for ...} です.浅いコピーでは内部の可変オブジェクトが共有されることがあるため,入れ子のデータを変更する場合は別途検討します.

出席簿から「偶数番号の人だけを取り,番号を2乗する」という作業を1行にしたのが内包表記です.元の出席簿を書き換えるのではなく,条件に合う新しい一覧を作ります.

chapter-10_code_16.py
squares = [n * n for n in range(6) if n % 2 == 0]
table = {n: n * n for n in range(4)}
unique = {n % 3 for n in range(6)}
a = [3, 1]
b = sorted(a)
print(squares, table, unique)
print(a, b)
実行結果の例
[0, 4, 16] {0: 0, 1: 1, 2: 4, 3: 9} {0, 1, 2}
[3, 1] [1, 3]

1行目は0から5の偶数を選んで2乗します.2行目は数とその平方を対応させる辞書を作ります.3行目は余りの重複を集合で除く.4行目と5行目は元のリスト a と並べ替えた新しいリスト b を作ります.最後の2行はそれぞれの結果を確認します.集合の表示順には依存しません.

そのまま演習内包表記とコピー · そのまま演習

そのまま演習:range(6) を range(8) に変え,平方の一覧に何が追加されるか調べます.

ヒントを見る

偶数だけを選ぶ条件を先に確認します。内包表記の形がリスト、辞書、集合のどれかにより結果の表し方が変わります。

演習の解説を見る

解説:追加される偶数は6なので,36 が末尾に加わる.

虫食い内包表記とコピー · 虫食い

虫食い演習:0から4までの数とその平方の辞書を作ります.

chapter-10_code_18.py
table = {n: n * n for n in ___(5)}
ヒントを見る

0から4までの整数を順に取り出し、各数を辞書のキーにします。5を終点として受け取る反復用の関数を考えます。

演習の解説を見る

解説:空欄は range です.range(5) は0から4までを作ります.

b = a はリストのコピーではなく,同じリストを二つの名前で参照します.たとえば a=[3,1]; b=a; b.append(2) とすると a も [3,1,2] になります.独立した浅いコピーが欲しい場合は b = a.copy() とします.sorted(a) も新しいリストを返します.

内包表記を普通の繰り返しに戻す

内包表記を読みにくく感じたら,一行を選択と追加の二段階に分解します.次の二つは同じ結果を作ります.

chapter-10_code_19.py
short = [x * x for x in range(6) if x % 2 == 0]
long = []
for x in range(6):
    if x % 2 == 0:
        long.append(x * x)
print(short)
print(long)
実行結果の例
[0, 4, 16]
[0, 4, 16]

1行目は0から5までの偶数の平方を一行で作ります.2行目は結果用の空リストを作ります.3行目は数を順に取り出します.4行目は偶数だけを通す.5行目は平方を追加します.6行目と7行目は二つの作り方の一致を確認します.

そのまま演習内包表記を普通の繰り返しに戻す · そのまま演習

range(6) を range(5) に変え、2つの一覧とそれぞれの長さを表示してください。

ヒントを見る

内包表記と通常の反復に同じ範囲を使います。選ばれた偶数と、その平方が両方の一覧で一致するか比べます。

演習の解説を見る

解説:どちらも偶数0,2,4を使うため [0, 4, 16] です.

虫食い内包表記を普通の繰り返しに戻す · 虫食い

for x in range(4) と print(x) は残します。奇数だけを表示する条件になるように、x % 2 と比較する空欄を補ってください。1と3を、この順に1行に1個ずつ表示します。

ヒントを見る

奇数を2で割った余りを考えます。割り算の商ではなく、余りを比較する条件です。

演習の解説を見る

解説:空欄は 1 です.2で割った余りが1のとき奇数となります.

文字列は位置,辞書はキー,集合は所属と共通部分に着目します.内包表記は新しい容器を作る構文であり,処理が複雑になったら通常の for 文に戻して読みやすくします.

用語を確かめる

文字列

読み:もじれつ.英語:string.糸につないだ文字の列という比喩から来る.関連語:添字,スライス.

添字

読み:そえじ.英語:index.本の索引が位置を示すのと同じ発想です.関連語:スライス,位置.

スライス

読み:すらいす.英語:slice.薄く切り出すという意味です.関連語:添字,半開区間.

正規表現

読み:せいきひょうげん.英語:regular expression.形を規則で表す語です.関連語:パターン,文字クラス.

タプル

読み:たぷる.英語:tuple.数を組にする語に由来します.関連語:組,代入.

辞書

読み:じしょ.英語:dictionary.単語を見出しから探す辞書の比喩です.関連語:キー,値.

キー

読み:きー.英語:key.値を探すための鍵という意味です.関連語:辞書,値.

集合

読み:しゅうごう.英語:set.要素を集めたものを意味します.関連語:共通部分,重複.

内包表記

読み:ないほうひょうき.英語:comprehension.条件に合う要素をまとめて記述します.関連語:リスト,辞書,集合.

用語を確かめる:言葉とコードの動きを結び付けます

知識

辞書(dictionary)はキー(key)と値(value)の対応を保持します。集合(set)は同じ要素を重複して保持しません。リストでは同じ値が複数回現れることがありますが、集合へ変換すると異なる値の種類を数えられます。集合の表示順を前提にしないよう、ここでは要素数を表示します。

scores = {"A": 80, "B": 60}
answers = ["A", "A", "B"]
print(scores["B"])
print(len(set(answers)))
そのまま演習用語を確かめる:言葉とコードの動きを結び付けます

辞書からキー"B"の点数を読み、次に回答リストに含まれる異なる回答の種類数を表示します。実行前に各行の値を予想し、コードをそのまま実行してください。その後「出力例」を開いて比較します。入力はありません。回答総数と、回答の種類数を区別します。

出力例
60
2
ヒントを見る

辞書から取り出す値と、リストを集合へ変えた後の要素数を別々に予想してください。回答が現れた回数と、異なる回答の種類数は同じとは限りません。

演習の解説

scoresのキーBに対応する値は60です。answersは3要素ですが、set(answers)にはAとBの2要素だけが残ります。lenでその要素数2を表示します。len(answers)なら回答総数3となり、この問題で求める種類数とは異なります。

虫食い用語を確かめる:言葉とコードの動きを結び付けます

___の1か所に、リストanswersから重複のない集合を作る組み込み関数名を補ってください。ほかの行は変更せず、60と2をそれぞれ別の行へ表示してください。

scores = {"A": 80, "B": 60}
answers = ["A", "A", "B"]
print(scores["B"])
print(len(___(answers)))
ヒントを見る

辞書の角括弧には値を探すキーを入れます。回答にはAが2回あります。種類を数えるときは、同じ回答を2種類として数えないための変換を考えてください。

虫食いの解説
scores = {"A": 80, "B": 60}
answers = ["A", "A", "B"]
print(scores["B"])
print(len(set(answers)))

空欄には set が入ります。scoresのキーBに対応する値は60です。answersは3要素ですが、set(answers)にはAとBの2要素だけが残ります。lenでその要素数2を表示します。len(answers)なら回答総数3となり、この問題で求める種類数とは異なります。

図で理解する

文字列のスライス
文字列のスライス
辞書の対応
辞書の対応
辞書から値を読む:考える前
辞書から値を読む:考える前
辞書から値を読む:書き込んだ後
辞書から値を読む:書き込んだ後

練習して確かめる

文字列の連結と繰り返し

知識

文字列どうしは + で連結できます。数値を混ぜるときは str で文字列化します。* と整数を使うと文字列を繰り返せる。

chapter-10_code_21.py
name = 'Python'
print(name + '入門')
print('-' * 5)
そのまま演習文字列の連結と繰り返し · そのまま演習

name を 'Taro' に変え、文字列の連結と繰り返しの結果を表示してください。

ヒントを見る

変更するのは名前の文字列です。入門との連結の行と、ハイフンを繰り返す行は残してください。

演習の解説

最初の行は文字列を結合し、次の行はハイフンを5回繰り返します。

虫食い文字列の連結と繰り返し · 虫食い

空欄を埋め、コードを実行してください。

chapter-10_code_22.py
age = 20
print('年齢:' + ___(age))
ヒントを見る

年齢の整数を文字列へ変えてから連結します。数値の足し算ではなく、文字列どうしの連結に型をそろえます。

虫食いの解説
chapter-10_code_23.py
age = 20
print('年齢:' + str(age))

str を補います。数値と文字列を直接 + でつなぐと TypeError になります。

文字列の位置とスライス

知識

文字列は0から始まる番号で1文字を取り出せます。s[a:b] は a 番目から b の直前までを取り出し、元の文字列は変わりません。

chapter-10_code_24.py
s = 'Python'
print(s[0], s[1:4])
そのまま演習文字列の位置とスライス · そのまま演習

既存の表示の次に s[-1] を1行で表示してください。

ヒントを見る

末尾から数える添字で最後の1文字を取り出します。最初の表示を置き換えず、その次に新しい表示を追加します。

演習の解説

s[0] は P、s[1:4] は yth、s[-1] は最後の n。

虫食い文字列の位置とスライス · 虫食い

空欄を埋め、コードを実行してください。

chapter-10_code_25.py
s = 'Python'
print(s[___:4])
ヒントを見る

先頭の番号は0です。終点4の文字は含まれないため、取り出したい文字列の最初の位置を確かめます。

虫食いの解説
chapter-10_code_26.py
s = 'Python'
print(s[1:4])

1 を補うと yth が得られます。

文字列のメソッド

知識

文字列の strip は前後の空白を除き、split は区切り文字で分割します。これらは新しい値を返し、元の文字列自体は変えません。

chapter-10_code_27.py
row = ' A, B, C '
print(row.strip().split(','))
そのまま演習文字列のメソッド · そのまま演習

元の分割結果に続け、各要素の前後の空白も除いた結果を1行で表示してください。

ヒントを見る

文字列全体に一度だけ空白除去する処理と、分割した各文字列へ空白除去する処理を比べます。二つの結果を別々に表示します。

演習の解説

strip は文字列全体の両端だけを除くため、B と C の前の空白は残る。

虫食い文字列のメソッド · 虫食い

空欄を埋め、コードを実行してください。

chapter-10_code_28.py
text = ' a b '
print(text.___())
ヒントを見る

text の内側の空白は残し、両端の空白だけを取り除きます。引数なしで使う文字列メソッドです。

虫食いの解説
chapter-10_code_29.py
text = ' a b '
print(text.strip())

strip を補います。

正規表現の入口

知識

規則的な文字列の検索には re モジュールを使います。re.fullmatch は文字列全体がパターンに一致するときだけ結果を返します。\d は数字1字を表します。

chapter-10_code_30.py
import re
print(bool(re.fullmatch(r'\d{3}', '123')))
そのまま演習正規表現の入口 · そのまま演習

'12a' に変えて一致するか調べます。

ヒントを見る

3文字あっても、その全てが数字でなければパターンに一致しません。最後の文字の種類に注目します。

演習の解説

数字3字の条件を満たさないので False。

虫食い正規表現の入口 · 虫食い

空欄を埋め、コードを実行してください。

chapter-10_code_31.py
import re
print(bool(re.___(r'\d{3}', '123')))
ヒントを見る

文字列全体が数字3文字という条件を満たすかを調べます。一部分だけを探す検索とは区別してください。

虫食いの解説
chapter-10_code_32.py
import re
print(bool(re.fullmatch(r'\d{3}', '123')))

fullmatch を補います。

正規表現の文字クラスと置換

知識

[0-9] は数字1字、+ は1回以上の繰り返しを示します。re.sub は一致した箇所を別の文字列に置き換える。入力形式の確認と文字列整形を区別して使います。

chapter-10_code_33.py
import re
print(re.sub(r'[0-9]+', '#', 'A12-B3'))
そのまま演習正規表現の文字クラスと置換 · そのまま演習

re.sub の置換文字列だけを '#' から 'X' に置き換えてください。正規表現、入力文字列 A12-B3、import と print は残します。数字の連続部分をそれぞれ1文字の X に置き換え、結果を1行で表示します。空白・空行を追加しません。

ヒントを見る

数字が2文字以上続いても、正規表現の + はその連続部分をひとまとまりとして扱います。置換する文字列と、検索するパターンを区別してください。

演習の解説

12 と 3 はそれぞれ数字のまとまりとして X に置き換わります。A、ハイフン、B は残るため、学習コードの出力は AX-BX の1行です。

虫食い正規表現の文字クラスと置換 · 虫食い

空欄を埋め、コードを実行してください。

chapter-10_code_34.py
import re
print(re.___(r'[0-9]+', '#', 'A12-B3'))
ヒントを見る

ここでは一致の真偽ではなく、数字のまとまりを別の文字列に置き換えます。パターン、置換文字列、対象の順に引数を読む関数です。

虫食いの解説
chapter-10_code_35.py
import re
print(re.sub(r'[0-9]+', '#', 'A12-B3'))

sub を補います。

タプルと多重代入

知識

タプルは作成後に要素を書き換えない列で、座標など一組の値に適します。多重代入を使えば複数の値を一度に受け取れます。

chapter-10_code_36.py
point = (3, 4)
x, y = point
print(x + y)
そのまま演習タプルと多重代入 · そのまま演習

point の定義と x, y = point は残します。print(x + y) の行を、新しいタプル point = (5, point[1]) を作る行と、その2要素を表示する行に置き換えてください。出力は半角空白で区切った5と4の1行です。タプルの要素へ直接代入せず、合計は表示しません。

ヒントを見る

タプルの中身を直接書き換える方法と、別のタプルを変数に入れ直す方法を区別します。変えるのは第1要素で、第2要素は元のタプルから取り出します。

演習の解説

新しいタプルを point に代入すると、第1要素は5、第2要素は4になります。x と y を代入し直したわけではないので、変更後の point の要素を表示します。学習コードの出力は5 4の1行です。

虫食いタプルと多重代入 · 虫食い

空欄を埋め、コードを実行してください。

chapter-10_code_37.py
point = (3, 4)
x, y = ___
print(x + y)
ヒントを見る

二つの値を受け取る代入の右側には、二つの要素を持つ既存のタプルを置きます。新しい数を作る必要はありません。

虫食いの解説
chapter-10_code_38.py
point = (3, 4)
x, y = point
print(x + y)

point を補います。

辞書と集合

知識

辞書はキーから値を取り出します。集合は重複をなくした要素の集まりで、共通部分には & を使えます。

chapter-10_code_39.py
scores = {'A': 80, 'B': 70}
print(scores['A'])
print({1, 2} & {2, 3})
そのまま演習辞書と集合 · そのまま演習

scores の定義を残し、print(scores['A']) の行を、C に90を代入する行と C の値を表示する行へ置き換えてください。最後の集合の共通部分を表示する行は残します。出力は90と集合の共通部分の2行で、空行を加えません。

ヒントを見る

辞書への追加では、存在しないキーを添字にして値を代入できます。変更する print と、最後の集合の共通部分を表示する print を区別してください。

演習の解説

C のキーを追加してその値を表示するため、1行目は90です。最後の集合の共通部分の表示は残しているため、2行目は {2} です。A の点数を表示する行は置き換えているので表示しません。

虫食い辞書と集合 · 虫食い

空欄を埋め、コードを実行してください。

chapter-10_code_40.py
scores = {'A': 80}
print(scores[___])
ヒントを見る

scores は辞書なので、数字の位置ではなく、登録済みの名前のキーを添字にします。キーの型は文字列です。

虫食いの解説
chapter-10_code_41.py
scores = {'A': 80}
print(scores['A'])

'A' を補います。

リスト内包表記

知識

リスト内包表記は変換と反復を一行にまとめる。条件付きなら必要な要素だけを選べる。長く複雑な式は通常のループに戻すと読みやすい。

chapter-10_code_42.py
squares = [n*n for n in range(5)]
print(squares)
そのまま演習リスト内包表記 · そのまま演習

偶数の平方だけを作る条件を追加します。

ヒントを見る

平方を計算する式と、偶数だけを選ぶ条件を分けて書きます。条件に通らなかった数は結果に入りません。

演習の解説

[n*n for n in range(5) if n%2==0] で [0,4,16]。

虫食いリスト内包表記 · 虫食い

空欄を埋め、コードを実行してください。

chapter-10_code_43.py
squares = [n*n ___ n in range(5)]
ヒントを見る

内包表記の計算式 n*n と、要素を取り出す n in range(5) をつなぐ構文です。通常の反復と対応させます。

虫食いの解説
chapter-10_code_44.py
squares = [n*n for n in range(5)]

for を補います。

破壊的操作とコピー

知識

append や sort は元のリストを変えます。sorted は新しいリストを返します。別名で同じリストを参照している場合の変化に注意します。

chapter-10_code_45.py
a = [3, 1]
b = sorted(a)
print(a, b)
そのまま演習破壊的操作とコピー · そのまま演習

b = sorted(a) の1行を a.sort() と b = a の2行へ置き換えてください。a の定義と print(a, b) は残します。a と b を半角空白で区切り、1行で表示します。

ヒントを見る

sorted は新しいリストを返しますが、sort は元のリストを書き換えます。sort の返り値を b に入れるとリストにならないため、並べ替えたあとで a を b に代入します。

演習の解説

a.sort() で a 自体が昇順になります。そのあと b = a とするため、2つの変数は同じ並べ替え済みリストを指します。出力は [1, 3] [1, 3] の1行です。

虫食い破壊的操作とコピー · 虫食い

空欄を埋め、コードを実行してください。

chapter-10_code_46.py
a = [3, 1]
b = ___(a)
ヒントを見る

a を変更せずに、並べ替えた新しいリストを b へ入れます。元のリストを書き換えるメソッドとの違いを確認します。

虫食いの解説
chapter-10_code_47.py
a = [3, 1]
b = sorted(a)

sorted を補います。

辞書・集合の内包表記

知識

内包表記はリスト以外にも使えます。波括弧でキー:値を書けば辞書、値だけを書けば集合になります。重複した集合要素は一つにまとまる。

chapter-10_code_48.py
squares = {n: n*n for n in range(4)}
print(squares)
そのまま演習辞書・集合の内包表記 · そのまま演習

{n%3 for n in range(8)} を作り、重複が残るか確かめる。

ヒントを見る

剰余の同じ値が何度もできても、集合では一つにまとまります。辞書を作る行と、集合を表示する行を区別します。

演習の解説

集合なので同じ余りは1つにまとまり {0,1,2} になります。

虫食い辞書・集合の内包表記 · 虫食い

空欄を埋め、コードを実行してください。

chapter-10_code_49.py
squares = {n: n*n ___ n in range(4)}
ヒントを見る

辞書内包表記でも、キーと値の式の後ろに反復を書く必要があります。コロンは既にキーと値を区切っています。

虫食いの解説
chapter-10_code_50.py
squares = {n: n*n for n in range(4)}

for を補います。

章末演習(10問)

ここには解答を載せていません。

  1. 章末演習章末演習 1

    【基本】「年-月-日」形式の日付文字列を「○年○月○日」の形に変換する関数を作ってください。

    入力

    入力なし(input は使わない)。変換する文字列は "2026-10-04","2025-01-15","2024-12-31" の3つ(この順)とします。どれも「4桁の年-2桁の月-2桁の日」の半角文字列で,形式の誤りはないものとします。

    処理条件

    • 関数 to_japanese_date(text) を def で定義します。split("-") で3つに分けて3つの変数で受け取り,それぞれを int に変換します。そのうえで「年」「月」「日」を付けた文字列を return する(月日の先頭の 0 は付かない)。
    • 年だけは別に,切り出し text[:4] で取り出して表示します。split の結果の先頭と一致することを assert で確かめる。
    • datetime モジュール,re モジュール,replace による 0 の除去は使いません。

    出力

    • 3行を表示します。各行は「変換結果 年だけ: 年」の形とします。変換結果と「年だけ:」の間,「年だけ:」と年の間は半角空白1個とし,数字とコロンは半角とします。
    • 見出し・空行・デバッグ用の表示は付けません。assert は書いてよい(成功したときは何も表示されない)。

    実行例

    2026年10月4日 年だけ: 2026
    2025年1月15日 年だけ: 2025
    2024年12月31日 年だけ: 2024
    
    ヒントを見る

    split の結果がいくつの要素を持つリストになるかを確かめ,その個数と同じ数の変数で受け取りましょう。文字列の "04" と整数の 4 で表示がどう違うかも試してみましょう。

  2. 章末演習章末演習 2

    【基本】文字列が学籍番号の形式かどうかを正規表現で判定する関数を作ってください。

    入力

    入力なし(input は使わない)。判定する文字列は次の6つ(この順)とします。 "B123456","b123456","B12345","C654321x","Z000000","B123456"(最後の文字は全角の6)

    処理条件

    • 学籍番号の形式は「半角英大文字1字+半角数字6字」とし,前後に余分な文字があってはなりません。
    • import re します。関数 is_student_id(text) の中で re.fullmatch を使って判定し,True または False(bool)を return します。
    • 全角数字は数字として認めません。
    • isupper,isdigit,文字コードの比較などによる判定は使いません。

    出力

    • 6行を表示します。各行は「文字列: 判定結果」の形とし,判定結果は True または False とします。コロンの後に半角空白を1個入れます。
    • 見出し・空行・デバッグ用の表示は付けません。assert は書いてよい(成功したときは何も表示されない)。

    実行例

    B123456: True
    b123456: False
    B12345: False
    C654321x: False
    Z000000: True
    B123456: False
    
    ヒントを見る

    fullmatch が一致しなかったときに何を返すかを確かめ,それを True / False に直す方法を考えましょう。数字を表す記号が全角の数字にも一致するかどうかを実際に試してから,パターンを決めましょう。

  3. 章末演習章末演習 3

    【基本】果物名のリストから,各果物の出現回数を数える辞書を作ってください。

    入力

    入力なし(input は使わない)。プログラムの中に次の変数を書きます。 fruits = ["apple", "banana", "apple", "cherry", "banana", "apple"]

    処理条件

    • 空の辞書 counts を用意します。for 文で fruits を1つずつ調べ,辞書の get メソッドを使って回数を1増やす。まだ登録されていないキーでもエラーにならないようにします。
    • collections.Counter,リストの count メソッド,in で登録済みかを調べる if 文は使いません。
    • 辞書を表示した後,キーを sorted で名前順(アルファベット順)に並べて1行ずつ表示します。

    出力

    • 4行を表示します。1行目は辞書を print した表記(キーは最初に現れた順)とします。2〜4行目は「果物名 回数」(半角空白1個区切り)を名前順に表示します。
    • 見出し・空行・デバッグ用の表示は付けません。assert は書いてよい(成功したときは何も表示されない)。

    実行例

    {'apple': 3, 'banana': 2, 'cherry': 1}
    apple 3
    banana 2
    cherry 1
    
    ヒントを見る

    get の第2引数は「キーが見つからなかったときに代わりに返す値」です。初めて出てきた果物では何を返してほしいかを考えましょう。

  4. 章末演習章末演習 4

    【応用】2つの授業の受講者を集合で表し,集合演算で共通部分・差・対称差・和を求めてください。

    入力

    入力なし(input は使わない)。プログラムの中に次の集合を書きます。 A = {"佐藤", "鈴木", "高橋", "田中"} B = {"鈴木", "田中", "伊藤"}

    処理条件

    • 次の4つを演算子 &,-,^,| を使って求める:(1) 両方を受講している人,(2) A だけを受講している人,(3) どちらか一方だけを受講している人,(4) 少なくとも一方を受講している人数(len で求める)。
    • for 文や if 文で要素を1つずつ調べる方法は使いません。
    • 集合には順序がないので,(1)〜(3) は sorted でリストにしてから表示します。並びは sorted の既定の順(文字コード順)であり,五十音順ではありません。

    出力

    • 4行を表示します。順に「両方: リスト」「Aだけ: リスト」「一方だけ: リスト」「合計人数: 人数」とします。コロンとその後の値の間は半角空白1個,コロン・A・数字は半角とします。
    • 見出し・空行・デバッグ用の表示は付けません。assert は書いてよい(成功したときは何も表示されない)。

    実行例

    両方: ['田中', '鈴木']
    Aだけ: ['佐藤', '高橋']
    一方だけ: ['伊藤', '佐藤', '高橋']
    合計人数: 5
    
    ヒントを見る

    2つの円が重なったベン図を描き,求める4つがそれぞれどの領域に当たるかを塗り分けてから,対応する演算子を選びましょう。

  5. 章末演習章末演習 5

    【応用】リスト内包表記・辞書内包表記・集合内包表記を使い分けてください。

    入力

    入力なし(input は使わない)。プログラムの中に次の変数を書きます。 numbers = [3, 8, 12, 5, 6, 7] words = ["apple", "avocado", "banana", "blueberry", "cherry"]

    処理条件

    • (1) リスト内包表記で,numbers のうち偶数だけを元の順に2乗したリストを作ります。
    • (2) 辞書内包表記で,「単語→文字数」の辞書を words の順に作ります。
    • (3) 集合内包表記で,各単語の先頭1文字の集合を作ります。
    • 3つとも内包表記1つで作ります。for 文と append の組み合わせ,dict()・set() に別のリストを渡す方法,map は使いません。

    出力

    • 3行を表示します。1行目は (1) のリスト,2行目は (2) の辞書を print した表記,3行目は (3) の集合を sorted でリストにした表記とします。
    • 見出し・空行・デバッグ用の表示は付けません。assert は書いてよい(成功したときは何も表示されない)。

    実行例

    [64, 144, 36]
    {'apple': 5, 'avocado': 7, 'banana': 6, 'blueberry': 9, 'cherry': 6}
    ['a', 'b', 'c']
    
    ヒントを見る

    まず for 文で書いた場合を頭の中で組み立て,「何を集めるか」「どこから取り出すか」「どんな条件で残すか」の3つを内包表記のどの位置に置くかを考えましょう。かっこの種類とコロンの有無で,できあがる型が変わります。

  6. 章末演習章末演習 6

    【応用】2次元リストの複製のしかたによって,元のリストが変わるかどうかを確かめてください。

    入力

    入力なし(input は使わない)。座席表 grid = [[0, 0], [0, 0]] を使います。

    処理条件

    • (1) shallow = grid.copy() で外側のリストだけを複製し,shallow[0][0] = 1 とします。その後,grid と,shallow[0] is grid[0] の結果を表示します。
    • (2) grid を [[0, 0], [0, 0]] で作り直します。リスト内包表記で各行も複製した deep = [row.copy() for row in grid] を作り,deep[0][0] = 1 とします。その後,grid,deep,deep[0] is grid[0] の結果を表示します。
    • copy モジュール(copy.deepcopy など)は使いません。

    出力

    • 2行を表示します。
    • 1行目:「浅いコピー後の grid: grid の内容 同じ行か: True または False」
    • 2行目:「行ごとコピー後の grid: grid の内容 deep: deep の内容 同じ行か: True または False」
    • リストは Python が print する表記とし,項目の間は半角空白1個,コロンは半角とします。
    • 見出し・空行・デバッグ用の表示は付けません。assert は書いてよい(成功したときは何も表示されない)。

    実行例

    浅いコピー後の grid: [[1, 0], [0, 0]] 同じ行か: True
    行ごとコピー後の grid: [[0, 0], [0, 0]] deep: [[1, 0], [0, 0]] 同じ行か: False
    
    ヒントを見る

    外側のリストと内側の行リストを別々の箱として図に描きましょう。複製によってどの箱が新しく作られ,どの箱が共有されたままかを矢印で追います。

  7. 章末演習章末演習 7

    【応用】文章の中から「¥」に続く金額をすべて取り出し,合計を求めてください。

    入力

    入力なし(input は使わない)。プログラムの中に次の変数を書きます。「¥」は1文字の円記号とします。金額は半角数字で,3桁ごとに半角カンマが入ることがあります。 text = "りんご¥120、メロン¥1,500、ぶどう¥680を買った"

    処理条件

    • import re します。re.findall とグループ(丸かっこ)を使い,¥ の直後にある数字とカンマの並びだけを文字列のリストとして取り出す(¥ は含めない)。
    • 取り出した各文字列からカンマを取り除いて int に変換したリストを,リスト内包表記で作ります。
    • 合計は sum で求めてよいでしょう。split で文字列を手作業で切り分ける方法は使いません。

    出力

    • 3行を表示します。1行目は findall の結果のリスト,2行目は整数のリスト,3行目は「合計 金額 円」とする(「合計」「円」は全角,数字と空白は半角)。
    • 見出し・空行・デバッグ用の表示は付けません。assert は書いてよい(成功したときは何も表示されない)。

    実行例

    ['120', '1,500', '680']
    [120, 1500, 680]
    合計 2300 円
    
    ヒントを見る

    パターンの中で「目印として一致させたい部分」と「結果として取り出したい部分」を分けて考えましょう。カンマを含む文字列をそのまま int に渡すとどうなるかも試してみましょう。

  8. 章末演習章末演習 8

    【複合】英文に出てくる単語の出現回数を数え,多い順に上位3語を表示してください。

    入力

    入力なし(input は使わない)。プログラムの中に次の変数を書きます。 text = "The cat and the dog. The dog runs, and the cat sleeps."

    処理条件

    • 大文字と小文字は区別しないので,lower で小文字にそろえます。
    • re.findall(r"[a-z]+", ...) で英小文字の並びを単語として取り出す(ピリオドやカンマは含めない)。
    • 辞書と get で単語ごとの回数を数える。collections.Counter とリストの count メソッドは使いません。
    • 並べ替えの基準を返す関数 order(item) を def で定義し,sorted(counts.items(), key=order) で「回数の多い順,同数なら単語のアルファベット順」に並べる。lambda は使いません。
    • 異なる単語の数は,取り出した単語のリストを集合にして len で求めます。

    出力

    • 4行を表示します。1〜3行目は上位3語を「単語 回数」(半角空白1個区切り)で表示し,4行目は「異なる単語: 個数」とします。
    • 見出し・空行・デバッグ用の表示は付けません。assert は書いてよい(成功したときは何も表示されない)。

    実行例

    the 4
    and 2
    cat 2
    異なる単語: 6
    
    ヒントを見る

    タプルどうしは先頭の要素から順に比べられます。「多い順」と「アルファベット順」は向きが逆です。昇順に並べるだけで両方を満たすには,回数をどう変形すればよいかを考えましょう。

  9. 章末演習章末演習 9

    【複合】(氏名, 科目, 点数) のタプルのリストから氏名ごとの平均点を求め,高い順に順位を付けて表示してください。

    入力

    入力なし(input は使わない)。プログラムの中に次の変数を書きます。 records = [("Aoi", "math", 80), ("Aoi", "eng", 70), ("Ren", "math", 90), ("Ren", "eng", 95), ("Mio", "math", 60), ("Mio", "eng", 85)]

    処理条件

    • for 文でタプルを3つの変数に分けて受け取り,「氏名→点数のリスト」の辞書 scores を作る(setdefault または get を使ってよい)。
    • 辞書内包表記で「氏名→平均点」の辞書 averages を作ります。平均は sum と len で求めてよいでしょう。statistics モジュールは使いません。
    • 平均点の高い順に氏名を並べ,enumerate(..., start=1) で 1 から順位を付けて表示します。このデータに同点はありません。
    • 集合内包表記で受講科目の集合を作り,sorted でリストにして表示します。

    出力

    • 4行を表示します。1〜3行目は「(順位)位 氏名 平均点」(例:1位 Ren 92.5)とし,平均点は丸めずに Python の既定の表示とします。4行目は科目のリストとします。
    • 「位」は全角,ほかは半角とします。項目の間は半角空白1個とします。
    • 見出し・空行・デバッグ用の表示は付けません。assert は書いてよい(成功したときは何も表示されない)。

    実行例

    1位 Ren 92.5
    2位 Aoi 75.0
    3位 Mio 72.5
    ['eng', 'math']
    
    ヒントを見る

    処理を「人ごとに点数を集める」「人ごとに平均する」「平均で並べる」の3段階に分けましょう。各段階の結果を一度表示して確かめてから次へ進みます。辞書を sorted に渡すと何が並ぶのかも確認しておきましょう。

  10. 章末演習章末演習 10

    【複合】「時刻 利用者 種類」の形のログを正規表現で解析し,利用者の一覧・エラー回数・不正行の数を求めてください。

    入力

    入力なし(input は使わない)。プログラムの中に次の変数を書きます。 logs = ["10:00 alice LOGIN", "10:05 bob ERROR", "10:07 alice ERROR", "10:09 carol LOGIN", "10:12 bob ERROR", "broken line"]

    処理条件

    • 各行に re.fullmatch(r"(\d\d:\d\d) (\w+) (LOGIN|ERROR)", 行) を適用します。一致しない行は不正行として数え,その行の残りの処理を飛ばす。
    • 一致した行は,groups() で時刻・利用者・種類の3つに分けて受け取ります。
    • 次の3つを求めます。(1) 利用者の集合(set の add で追加する)。(2) 利用者ごとの ERROR 回数の辞書(get で数え,ERROR が0回の利用者はキーに入れない)。(3) 不正行の数。
    • split による分解や collections.Counter は使いません。

    出力

    • 3行を表示します。順に次のとおりとします。
    • 「利用者: 利用者のリスト」(集合を sorted でリストにする)
    • 「エラー回数: 辞書」(最初に ERROR が現れた順の print 表記)
    • 「不正行: 個数」
    • コロンの後は半角空白1個とし,英数字・記号は半角とします。
    • 見出し・空行・デバッグ用の表示は付けません。assert は書いてよい(成功したときは何も表示されない)。

    実行例

    利用者: ['alice', 'bob', 'carol']
    エラー回数: {'bob': 2, 'alice': 1}
    不正行: 1
    
    ヒントを見る

    まず1行だけに fullmatch を適用し,一致したときと一致しなかったときに返るものを表示して確かめましょう。不正行を先に処理してループの次へ進めば,残りの処理は正しい行だけを前提に書けます。