トラック
/
Python
Python
/
演習
/
カードゲーム
カードゲーム

カードゲーム

学習演習

はじめに

listは、項目を_順番_に並べて持つ、変更可能なコレクションです。 ほとんどのコレクション(組み込みのtuple、dict、setを参照)と同様に、配列はあらゆるデータ型(複数のデータ型でもかまいません)への参照を保持できます。他の配列を入れることもできます。 シーケンスと同じように、項目には左からの0-based index番号と、右からの-1-based index番号でアクセスできます。 配列は、スライス記法または<list>.copy()を使って、全体または一部をコピーできます。

配列は、min()/max()、<list>.index()、<list>.append()、<list>.reverse()のような共通のシーケンス操作と変更可能なシーケンス操作の両方をサポートします。 配列の要素は、for item in <list>という構文を使って繰り返し処理できます。 要素のインデックスと要素の値の両方が必要なときは、for index, item in enumerate(<list>)を使えます。

内部的には、listsは動的配列として実装されています。JavaのArrayList型に似ており、長さがわからない同じようなデータ(文字列、数値、セットなど)のまとまりを格納するのによく使われます。 配列は非常に柔軟で便利なデータ構造で、Pythonの多くの組み込みメソッドや操作は、出力として配列を生成します。

作成

listは、角括弧([])と要素の間のカンマを使って_リテラル_として宣言できます:

>>> no_elements = []

>>> no_elements
[]

>>> one_element = ["Guava"]

>>> one_element
['Guava']

>>> elements_separated_with_commas = ["Parrot", "Bird", 334782]

>>> elements_separated_with_commas
['Parrot', 'Bird', 334782]

読みやすくするために、listの中に要素や入れ子になったデータ構造が多いときは、改行を使えます:

>>> lots_of_entries = [
      "Rose",
      "Sunflower",
      "Poppy",
      "Pansy",
      "Tulip",
      "Fuchsia",
      "Cyclamen",
      "Lavender"
   ]
   
>>> lots_of_entries
['Rose', 'Sunflower', 'Poppy', 'Pansy', 'Tulip', 'Fuchsia', 'Cyclamen', 'Lavender']

# Each data structure is on its own line to help clarify what they are.
>>> nested_data_structures = [
      {"fish": "gold", "monkey": "brown", "parrot": "grey"},
      ("fish", "mammal", "bird"),
      ['water', 'jungle', 'sky']
   ]
   
>>> nested_data_structures
[{'fish': 'gold', 'monkey': 'brown', 'parrot': 'grey'}, ('fish', 'mammal', 'bird'), ['water', 'jungle', 'sky']]

list()コンストラクターは、引数なしで使うことも、_イテラブル_を引数として使うこともできます。 イテラブル内の要素はコンストラクターによって順に取り出され、順番にlistへ追加されます:

>>> no_elements = list()

>>> no_elements
[]

# The tuple is unpacked and each element is added.
>>> multiple_elements_from_tuple = list(("Parrot", "Bird", 334782))

>>> multiple_elements_from_tuple
['Parrot', 'Bird', 334782]

# The set is unpacked and each element is added.
>>> multiple_elements_from_set = list({2, 3, 5, 7, 11})

>>> multiple_elements_from_set
[2, 3, 5, 7, 11]

listコンストラクターをstringやdictと一緒に使ったときの結果は、意外に思えるかもしれません:

# String elements (Unicode code points) are iterated through and added *individually*.
>>> multiple_elements_string = list("Timbuktu")

>>> multiple_elements_string
['T', 'i', 'm', 'b', 'u', 'k', 't', 'u']

# Unicode separators and positioning code points are also added *individually*.
>>> multiple_code_points_string = list('अभ्यास')

>>> multiple_code_points_string
['अ', 'भ', '्', 'य', 'ा', 'स']

# The iteration default for dictionaries is over the keys, so only key data is inserted into the list.
>>> source_data = {"fish": "gold", "monkey": "brown"}

>>> multiple_elements_dict_1 = list(source_data)
['fish', 'monkey']

listコンストラクターは_イテラブル_(または何もなし)しか引数に取らないため、_イテラブルでない_オブジェクトは型エラーを発生させます。 そのため、要素が1つのlistは、リテラルで作るほうがずっと簡単です。

# Numbers are not iterable, and so attempting to create a list with a number passed to the constructor fails.
>>> one_element = list(16)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable

# Tuples *are* iterable, so passing a one-element tuple to the constructor does work, but it's awkward
>>> one_element_from_iterable = list((16,))

>>> one_element_from_iterable
[16]

要素へのアクセス

配列内の項目(他のシーケンス型であるstrやtupleの項目も同様)には、0-based indexと_角括弧記法_でアクセスできます。 インデックスは、leftからright(0から始まる)、または**rightからleft**(-1から始まる)へ進みます。

左からのインデックス ⟹






0
👇🏾
1
👇🏾
2
👇🏾
3
👇🏾
4
👇🏾
5
👇🏾
P y t h o n
👆🏾
-6
👆🏾
-5
👆🏾
-4
👆🏾
-3
👆🏾
-2
👆🏾
-1





⟸ 右からのインデックス
>>> breakfast_foods = ["Oatmeal", "Fruit Salad", "Eggs", "Toast"]

# Oatmeal is at index 0 or index -4.
>>> breakfast_foods[0]
'Oatmeal'

>>> breakfast_foods[-4]
'Oatmeal'

# Eggs are at index -2 or 2
>>> breakfast_foods[-2]
'Eggs'

>>> breakfast_foods[2]
'Eggs'

# Toast is at -1
>>> breakfast_foods[-1]
'Toast'

list内の要素の一部には、スライス記法(<list>[start:stop])でアクセスできます。 _スライス_は、start <= index < stopとなる位置indexの要素の並びとして定義されます。 _スライス操作_では、切り出した項目のコピーが返り、元のlistは変更されません。

[start:stop:step]のようにstepパラメーターを使うと、listの要素を「飛ばしながら」取り出したり、絞り込んだりできます(たとえば、stepを2にすると、範囲内の1つおきの要素が選ばれます):

>>> colors = ["Red", "Purple", "Green", "Yellow", "Orange", "Pink", "Blue", "Grey"]

# If there is no step parameter, the step is assumed to be 1.
>>> middle_colors = colors[2:6]

>>> middle_colors
['Green', 'Yellow', 'Orange', 'Pink']

# If the start or stop parameters are omitted, the slice will
# start at index zero, and will stop at the end of the list.
>>> primary_colors = colors[::3]

>>> primary_colors
['Red', 'Yellow', 'Blue']

配列を扱う

配列に対して組み込みのsum()関数を使うと、配列内のすべての数値の合計が戻り値として返ります:

>>> number_list = [1, 2, 3, 4]
>>> sum(number_list)
10

len()関数を使えば、配列の_長さ_も取得できます:

>>> long_list = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]
>>> len(long_list)
10

配列は、さまざまな方法で組み合わせることもできます:

# Using the plus + operator unpacks each list and creates a new list, but it is not efficient.
>>> new_via_concatenate = ["George", 5] + ["cat", "Tabby"]

>>> new_via_concatenate
['George', 5, 'cat', 'Tabby']

# Likewise, using the multiplication operator * is the equivalent of using + n times.
>>> first_group = ["cat", "dog", "elephant"]
>>> multiplied_group = first_group * 3

>>> multiplied_group
['cat', 'dog', 'elephant', 'cat', 'dog', 'elephant', 'cat', 'dog', 'elephant']

配列は_イテレーター_を提供し、他の_シーケンス型_と同じようにループできます。

#  Looping through the list and printing out each element.
>>> colors = ["Orange", "Green", "Grey", "Blue"]

>>> for item in colors:
...     print(item)
...
Orange
Green
Grey
Blue

loopsとiteratorsについてのより詳しい説明は、loopsのコンセプトを完了してください。

説明

Elyseは、今度のラスベガス旅行でポーカー(やその他のカードゲーム)を楽しむのをとても心待ちにしています。 「自己記録」が大好きなので、記録作業を助けてくれる小さな関数をいくつか作りたいと考えており、その内容を一緒に考えてほしいと頼んできました。

1. ポーカーのラウンドを記録する

Elyseはとくにポーカーが好きで、自分が何ラウンドプレイしたのか、そしてそれが_どのラウンド_なのかを記録したいと考えています。 ラウンドにはそれぞれ番号があり、どのテーブルにも現在プレイ中のラウンド番号が表示されます。 Elyseはテーブルを選んで席に着き、最初のラウンドをプレイします。3ラウンドプレイする予定です。

現在のラウンド番号を受け取り、そのラウンドとこれから来る_次の2つ_を含む1つのlistを返す関数get_rounds(<round_number>)を実装しましょう。

>>> get_rounds(27)
[27, 28, 29]

2. すべてのラウンドを同じ場所にまとめる

Elyseは最初のテーブルで何ラウンドかプレイし、休憩してから2つ目のテーブルでも何ラウンドかプレイしました……が、テーブルごとに別々のリストになってしまいました! 彼女は2つのリストを1つにまとめて、すべてのポーカーのラウンドを同じ場所で記録できるようにしたいと考えています。

2つのリストを受け取り、1つ目のリストのすべてのラウンドに続けて2つ目のリストのすべてのラウンドを並べた1つのlistを返す関数concatenate_rounds(<rounds_1>, <rounds_2>)を実装しましょう。

>>> concatenate_rounds([27, 28, 29], [35, 36])
[27, 28, 29, 35, 36]

3. 以前のラウンドを探す

以前のポーカーのラウンドについて話していると、別のプレイヤーが、そのうちの2つはとても似た展開だったと口にしました。 Elyseは、自分がそのラウンドをプレイしたのかどうか、よくわかりません。

プレイしたラウンドのリストとラウンド番号という2つの引数を取る関数list_contains_round(<rounds>, <round_number>)を実装しましょう。 この関数は、そのラウンドがプレイしたラウンドのリストに含まれていればTrue、含まれていなければFalseを返します。

>>> list_contains_round([27, 28, 29, 35, 36], 29)
True

>>> list_contains_round([27, 28, 29, 35, 36], 30)
False

4. カードの値の平均を求める

ElyseはBlack Joeという新しいゲームを試してみたいと思っています。 これはブラックジャックに似ています。ブラックジャックでは、手札のカードの合計を目標の値にすることが目的ですが、Black Joeでは、カードの値の_平均_を7にすることが目的です。 平均は、すべてのカードの値を合計し、その合計を手札の枚数で割ることで求められます。

Black Joeの手札の平均値を返す関数card_average(<hand>)を実装しましょう。

>>> card_average([5, 6, 7])
6.0

5. 別の平均の求め方

Black Joeではスピードが重要です。Elyseは、平均をもっと速く求める方法を探そうとしています。

彼女は、_平均のような_値を求める2つの方法を思いつきました。

  • 手札の_最初_と_最後_の値の平均を取る。
  • 手札の中央値(真ん中のカード)を使う。

手札のカードの値が入ったリストhandを渡すので、関数approx_average_is_average(<hand>)を実装しましょう。

上に挙げたどちらか_一方_or_両方_の方法で得られる値が、_実際の平均_と_等しい_場合はTrueを返します。

注: 中央値を求めやすくするため、すべての手札の枚数は奇数です。

>>> approx_average_is_average([1, 2, 3])
True

>>> approx_average_is_average([2, 3, 4, 8, 8])
True

>>> approx_average_is_average([1, 2, 3, 5, 9])
False

6. さらに平均を求めるテクニック

平均の実験の結果に興味をそそられたElyseは、_偶数_の位置にあるカードの平均と_奇数_の位置にあるカードの平均を取ったら、同じ結果になるのだろうかと考えています。 またテスト用の関数を作る時間です!

偶数インデックスのカードの平均が、奇数インデックスのカードの平均と同じかどうかを示す真偽値を返す関数average_even_is_average_odd(<hand>)を実装しましょう。

>>> average_even_is_average_odd([1, 2, 3])
True

>>> average_even_is_average_odd([1, 2, 3, 4])
False

7. ボーナスラウンドのルール

Black Joeでは11回目の手札がボーナス手札で、ボーナスルールがあります。引いた最後のカードがジャックなら、その値を2倍にします。

手札を受け取り、最後のカードがジャック(11)かどうかを調べる関数maybe_double_last(<hand>)を実装しましょう。 最後のカードがジャック(11)なら、手札を返す前にその値を2倍にします。

>>> hand = [5, 9, 11]
>>> maybe_double_last(hand)
[5, 9, 22]

>>> hand = [5, 9, 10]
>>> maybe_double_last(hand)
[5, 9, 10]
GitHubで編集する リンクは新しいウィンドウまたはタブで開きます
Python Exercism

カードゲームを始める準備はできましたか?

Exercismに登録すれば、17個のコンセプト146個の演習、そして本物の人間によるメンタリングとともに、Pythonを学んでマスターできます。すべて無料です。