はじめに
どうもニコイチです。Pythonの学習を進めていく中で、リストやタプルのような複数の要素を持つデータに対して、繰り返し処理を行うためにforループを使う場面は非常に多いですよね。
# 例えば、購入金額のリストからポイントを計算する
purchase_records = [580, 1250, 990, 2400, 350]
total_points = 0
for price in purchase_records:
# 100円ごとに1ポイント付与
point = price // 100
total_points += point
print(f"合計ポイント: {total_points}") # 合計ポイント: 49
このforループを使ったコードは、プログラミングの基本であり、間違いなく動作します。しかし、「もっと短く、スマートに書けないだろうか?」と感じたことはありませんか?
もしあなたが、
- forループの記述を少し冗長だと感じている
- 他の人が書いた短いコードの意味が分からず、もどかしい思いをしたことがある
- より「Pythonらしい」コードを書けるようになりたい
と思っているなら、この記事はまさにうってつけです。
この記事では、forループからの脱却を目指し、map関数とリスト内包表記という、Pythonプログラマにとって必須とも言える2つの強力な武器を紹介します。この記事を読み終える頃には、あなたのコードはより簡潔で、読みやすく、そして何より「Pythonらしく」なっているはずです。
なぜforループからステップアップするのか?
先ほどのforループの例をもう一度見てみましょう。
total_points = 0 # 1. 合計を保持する変数を初期化
for price in purchase_records: # 2. リストを一つずつ取り出す
point = price // 100 # 3. 各要素を変換する
total_points += point # 4. 合計に加算する
このコードの本質的な目的は「購入金額リストの各要素をポイントに変換し、その合計を求める」ことです。しかし、コードの中にはtotal_pointsの初期化や、ループ内での加算処理など、目的を達成するための「手続き」に関する記述が多く含まれています。
このような「どうやるか(How)」を一つひとつ記述するスタイルを命令的プログラミングと呼びます。一方、Pythonが好むのは、「何をするか(What)」を簡潔に表現する宣言的プログラミングのスタイルです。
map関数やリスト内包表記は、この宣言的なスタイルを実現し、「手続き」の部分を隠蔽して「やりたいこと」だけをコードに表現するための強力なツールなのです。
実装コードと詳細な説明
それでは、先ほどのポイント計算を例に、forループをより良い形に書き換えていきましょう。
Step 1: map関数で処理を適用してみよう
mapは、リストなどの各要素に一律の処理(関数)を適用したい場合に非常に便利な関数です。
基本構文:
map(適用したい関数, 対象となるリストなど)
これを使ってポイント計算を書き換えてみます。
purchase_records = [580, 1250, 990, 2400, 350]
# 各購入金額をポイントに変換する関数を定義
def to_point(price):
return price // 100
# mapを使って、各要素に関数を適用
# [5, 12, 9, 24, 3] という結果の集まり(イテレータ)が生成される
point_iterator = map(to_point, purchase_records)
# sum()で合計する
total_points = sum(point_iterator)
print(f"合計ポイント: {total_points}") # 合計ポイント: 49
forループが消え、コードが「to_pointという処理をpurchase_recordsに適用し(map)、その結果を合計する(sum)」という、やりたいことの宣言に近くなりました。
ちなみに、input()で複数の数値を読み取る際によく使われる map(int, input().split()) も、split()で得られた文字列のリストの各要素にintという関数を適用している、というわけです。
Step 2: 本命!リスト内包表記を使いこなす
mapも便利ですが、Pythonの世界でさらに広く使われているのがリスト内包表記です。mapよりも柔軟性があり、多くの場合でより直感的に記述できます。
基本構文:
[式 for 要素 in 対象となるリストなど]
リスト内包表記を使って、ポイント計算のコードを書き換えてみましょう。
purchase_records = [580, 1250, 990, 2400, 350]
# [price // 100 という計算を、purchase_recordsの各要素priceに対して行う]
# [5, 12, 9, 24, 3] という新しいリストが生成される
point_list = [price // 100 for price in purchase_records]
total_points = sum(point_list)
print(f"合計ポイント: {total_points}") # 合計ポイント: 49
mapのためにわざわざ関数を定義する必要がなくなり、[]の中に自然な英語に近い順番で処理を書けるため、非常に分かりやすいですね。
さらに、sum()のような集計関数と組み合わせる場合は、[]を省略して()で囲む(あるいは省略する)ことで、ジェネレータ式として扱えます。これはメモリ効率が良いため、推奨される書き方です。
# これが最もPythonらしい書き方!
total_points = sum(price // 100 for price in purchase_records)
print(f"合計ポイント: {total_points}") # 合計ポイント: 49
最初のforループのコードが、たった1行になりました!
Step 3: 条件分岐も追加できる(フィルタリング)
リスト内包表記の真価は、条件(if)を簡単に追加できる点にもあります。
構文:
[式 for 要素 in リスト if 条件]
例えば、「1000円以上の買い物だけをポイント対象にする」というルールを追加してみましょう。
purchase_records = [580, 1250, 990, 2400, 350]
# 1000円以上のものだけを対象にポイント計算
total_points = sum(price // 100 for price in purchase_records if price >= 1000)
print(f"合計ポイント: {total_points}") # 合計ポイント: 36 (12 + 24)
forループで書くとif文が一つ増えて複雑になるところを、リスト内包表記なら自然な形で追加できます。
注意点・使い分け
- mapはイテレータを返す: mapが返す結果はリストではなく、「イテレータ」と呼ばれる特殊なオブジェクトです。結果をリストとして使いたい場合はlist(map(…))のように明示的に変換する必要があります。
- 巨大なリストとメモリ: リスト内包表記[…]は、結果をすべてメモリ上に保持するリストとして作成します。非常に巨大なデータを扱う場合、メモリを大量に消費する可能性があります。その場合はsum()の例で示したジェネレータ式(…)を使いましょう。
map vs リスト内包表記:
- 単純な関数適用: 既存の関数を適用するだけならmapも簡潔です。
- 柔軟性と可読性: 処理が少しでも複雑になる場合(簡単な計算やifでのフィルタリングなど)は、リスト内包表記の方がはるかに柔軟で読みやすいコードになります。迷ったら、まずはリスト内包表記を検討するのがおすすめです。
- 無理は禁物: 処理が複雑になりすぎる場合は、無理に1行で書こうとせず、素直にforループを使う方がかえって分かりやすくなります。読みやすさが第一です。
まとめ
今回は、初級者のforループから一歩進んで、よりPythonらしいコードを書くためのmap関数とリスト内包表記について学びました。
- forループは命令的で、手続きを記述する。
- mapは、既存の関数をリストの全要素に適用するのに便利。
- リスト内包表記は、データ変換とフィルタリングを1行で宣言的に書ける、非常に強力でPythonらしい機能。
- これらを使いこなすことが、中級者への大きな一歩となる。
最後に、記事のきっかけとなった、目標ポイントまでの最低購入金額を計算するコード全体を見てみましょう。
def min_purchase_to_point(M, record):
# 【ここがポイント!】ジェネレータ式で現在のポイントをスマートに計算
current_point = sum(x // 100 for x in record)
if current_point >= M:
return 0
# 不足ポイントと、それを稼ぐための最低購入金額を計算
need_point = M - current_point
return need_point * 100
# --- 実行部分 ---
M = 100 # 目標ポイント
record = [580, 1250, 990, 2400, 350] # 現在の購入記録
required_amount = min_purchase_to_point(M, record)
print(f"目標まであと{required_amount}円の購入が必要です。")
# -> 目標まであと5100円の購入が必要です。
sum(…)の部分が、いかにこのコードの可読性を高めているかが実感できるかと思います。
おわりに
「習うより慣れよ」という言葉の通り、これらのテクニックは実際に使ってみることで初めて身につきます。ぜひ、あなたがこれまでに書いたforループのコードを、リスト内包表記で書き換えられないか試してみてください。きっと、コードが洗練されていく楽しさを感じられるはずです。
この記事が、あなたのPythonプログラミングを次のレベルへ引き上げる一助となれば幸いです。
コメント