#!/usr/bin/env python
# coding: utf-8
# (ch:list)=
# # リスト
# ## 基本
# [リスト](https://docs.python.org/ja/3/library/stdtypes.html?highlight=list#sequence-types-list-tuple-range)は複数の値・オブジェクトをまとめる。要素を`,`で区切って並べ、全体を`[` `]`で囲んで作成する。
# In[1]:
a = [0, 1, 4, 9, 16, 25, 36, 49]
# In[2]:
a
# In[3]:
type(a)
# 先頭要素のインデックス番号は0。
# In[4]:
a[0]
# In[5]:
a[1]
# リストの要素数は[len](https://docs.python.org/ja/3/library/functions.html#len)関数で取得する。
# In[6]:
len(a)
# 要素の範囲を超えてリストにアクセスするとエラーになる。
# In[7]:
a[8]
# リストの最後の要素のインデックス番号は「要素数 - 1」。
# In[8]:
a[len(a)-1]
# リストの要素を末尾からアクセスするため、負のインデックス番号を用いることができる。
# In[9]:
a[-1]
# In[10]:
a[-2]
# 空のリストは`[]`か[list](https://docs.python.org/ja/3/library/stdtypes.html#list)関数で作成する。
# In[11]:
a = []
a
# In[12]:
a = list()
a
# ## スライス
# `a[start:end:step]`という形式で、リストの要素の部分列を抽出できる(「スライス」と呼ばれる)。
# + `start`: 抽出を開始する要素のインデックス番号。省略された場合は`0`となる。
# + `end`: 抽出を終了する要素のインデックス番号(ただし、`a[end]`の要素は含まれない)。省略された場合はリストの要素数となる。
# + `step`: 要素を抽出する間隔。省略された場合は`1`である。
# In[13]:
a = [0, 1, 4, 9, 16, 25, 36, 49]
# 開始位置と終了位置を指定したスライス。
# In[14]:
a[2:5]
# In[15]:
a[0:3]
# 開始位置を省略したスライス。
# In[16]:
a[:3]
# 終了位置を省略したスライス。
# In[17]:
a[5:]
# 負のインデックス番号を用いてもよい。
# In[18]:
a[-3:]
# 要素を取り出す間隔を指定したスライス。
# In[19]:
a[1::2]
# 間隔に`-1`を指定して、逆順に要素を取り出すスライス(`a[5]`, `a[4]`, `a[3]`が取り出される)。
# In[20]:
a[5:2:-1]
# 開始位置と終了位置を省略し、全ての要素を逆順に取り出すスライス。
# In[21]:
a[::-1]
# ## リストの要素に対する繰り返し
# In[22]:
a = [0, 1, 4, 9, 16, 25, 36, 49]
# リストの要素に先頭から繰り返しアクセスする。
# In[23]:
for x in a:
print(x)
# リストの要素に先頭から繰り返しアクセスすると同時に、各要素のインデックス番号を得る。
# In[24]:
for i, x in enumerate(a):
print(i, x, a[i])
# リストの要素に末尾から繰り返しアクセスする。
# In[25]:
for x in reversed(a):
print(x)
# ## リストの要素に対する操作
# In[26]:
wd = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday']
wd
# リスト内の要素を変更する。
# In[27]:
wd[1] = 'tue'
wd
# 末尾に要素を追加する。
# In[28]:
wd = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday']
wd.append('saturday')
# In[29]:
wd
# 指定した要素を削除する。
# In[30]:
del wd[5]
# In[31]:
wd
# リストを連結して、新しいリストを作成する。
# In[32]:
d = wd + ['saturday', 'sunday']
# In[33]:
d
# リストをスタックと見なしてpop操作をすると、末尾の要素が取り出され、同時に末尾の要素がリストから削除される。
# In[34]:
d.pop()
# In[35]:
d
# push操作に相当するものはappend関数である。
# In[36]:
d.append('sun')
d
# ## 要素の並び替え
#
# 要素を降順に並べたリストを新たに作成する(この場合は辞書順に並ぶ)。
# In[37]:
sorted(d)
# 元のリスト(`d`)は変更されていない。
# In[38]:
d
# 引数reverseを`True`にセットすると、要素を昇順に並べたリストを新たに作成できる(この場合は辞書の逆順に並ぶ)。
# In[39]:
sorted(d, reverse=True)
# 並び替えにおける要素の並び順は要素に対する比較演算子`<`の結果に基づく。例えば、`'saturday' < 'sun'`が成り立つので、`'saturday'`が`'sun'`の前に並ぶ。要素を比較するときの基準を変更したいときは、引数keyを用いる。以下の例では、要素の長さ(文字数)を取得する無名関数(lambda)を定義し、それをkey引数に渡すことで、文字数の少ない要素の順に並び替えている。
# In[40]:
sorted(d, key=lambda x: len(x))
# 引数reverseと併用して、文字数の多い要素の順に並び替える例である。
# In[41]:
sorted(d, key=lambda x: len(x), reverse=True)
# 要素を降順に並び替える。今回は、リスト`d`の要素そのものが並び替えられる。
# In[42]:
d.sort()
d
# 要素を昇順に並び替える。
# In[43]:
d.sort(reverse=True)
d
# 引数keyに関数を指定して、要素の並べ方を変更できる。
# In[44]:
d.sort(key=lambda x: len(x))
d
# ## 要素の所属検査
#
# ある要素がリストに含まれるかを調べる。
# In[45]:
'sun' in d
# ある要素がリストに含まれないかを調べる。
# In[46]:
'sun' not in d
# ## リストと参照
# ### 参照の代入
# リスト`x`を作成する。
# In[47]:
x = ['mon', 'tue', 'wed', 'thu', 'fri']
x
# 変数`y`を作成し、リストである`x`を代入する。
# In[48]:
y = x
y
# リスト`y`に要素を追加してみる。
# In[49]:
y.append('sat')
y.append('sun')
y
# (予想に反し)リスト`x`の要素も変更されている(`'sat'`と`'sun'`が追加されている)。
# In[50]:
x
# これは、代入文`y = x`が「変数`y`を定義し、変数`y`の参照先を変数`x`の参照先と一致させる」という動作を行うからである。元々、変数`x`はリストオブジェクトを参照していた(関連付けられていた)ので、そのリスト・オブジェクトに複数の変数`x`と`y`を経由してアクセスできるようになった。ひとつのオブジェクトの実体を複数の変数`x`と`y`が参照している状況であるので、いずれの変数を経由してもオブジェクトの内容を変更できるし、いずれの変数を評価しても同じ結果が得られる。
# ### リストのコピー
#
# 先ほどと同様に、リスト`x`を作成する。
# In[51]:
x = ['mon', 'tue', 'wed', 'thu', 'fri']
x
# 変数`y`を作成し、リストである`x`の全体のスライス`x[:]`を代入する。このとき、`x`のリスト・オブジェクトのコピーが作成され、変数`y`はコピーを参照することになる。
# In[52]:
y = x[:]
y
# 先ほどと同様に、リスト`y`に要素を追加してみる。
# In[53]:
y.append('sat')
y.append('sun')
# In[54]:
y
# 今回は、リスト`x`の要素は変更されない。これは、変数`x`と`y`が、それぞれ別のリスト・オブジェクトを参照しているからである。
# In[55]:
x
# ## リストと関数の引数
# 引数で与えられたリストの末尾に`'END'`を追加する関数append_endを定義する。
# In[56]:
def append_end(s):
s.append('END')
# 以下のリスト`x`を引数として、関数append_endを呼び出す。
# In[57]:
x = ['one', 'two']
x
# In[58]:
append_end(x)
# すると、呼び出し元の変数である`x`の内容が変化する。
# In[59]:
x
# 数値や文字列を引数として関数に渡す場合と異なり、リストを引数として関数に渡すと、その関数内でそのリストに対する変更は、関数の呼び出し元にも波及する。
# ## リストの様々な作成方法
# [range](https://docs.python.org/ja/3/library/stdtypes.html#range)関数が表す範囲に対応するリストを作成するには、[list](https://docs.python.org/ja/3/library/stdtypes.html#list)関数を用いる。
# In[60]:
a = list(range(10))
a
# In[61]:
a = list(range(1, 10, 2))
a
# リストの要素はデータ型が異なってもよい。
# In[62]:
b = [1, 'one', 'first']
# In[63]:
b[0]
# In[64]:
b[1]
# (0を含む)自然数のリストを[range](https://docs.python.org/ja/3/library/functions.html#func-range)関数で作成する。
# In[65]:
a = list(range(10))
a
# ### リストの内包表記
# 空のリストを作成する。
# In[66]:
a2 = []
a2
# 空のリスト(`a2`)に要素を追加する
# In[67]:
for i in range(10):
a2.append(i * 2)
a2
# これは2の段の掛け算の結果を表している。
# In[68]:
a2[7]
# 上記の処理はリストの内包表記を使って簡潔に書ける。ソースコードを左から"list of i times two for all i in the range of ten"のように英語読みをすると解釈しやすい。
# In[69]:
a2n = [i * 2 for i in range(10)]
a2n
# In[70]:
a2n[7]
# 3の段の掛け算も内包表記を使って簡潔に書ける。
# In[71]:
[i * 3 for i in range(10)]
# 内包表記を入れ子にして、掛け算表を2次元配列(リスト)として作成する。
# In[72]:
m = [[i * j for i in range(10)] for j in range(10)]
# In[73]:
m
# In[74]:
m[2]
# In[75]:
m[2][3]
# ### 2次元配列作成時の注意
# `*`演算子を使うと、指定した値で初期化しながら、指定した要素数のリストを作ることができる。
# In[76]:
a = [0] * 10
a
# これを拡張して、2次元配列を作ることもできるが、これは避けるべきである。
# In[77]:
a = [[0] * 10] * 10
a
# 一見すると2次元配列が作成されたように見えるが、思わぬ落とし穴がある。以下のコードである要素を変更したつもりが、列全体の値が変更されてしまう。
# In[78]:
a[3][5] = 2
a
# これは、`[0] * 10`で1次元のリスト・オブジェクトが一つだけ作成され、その唯一のオブジェクトへの参照が全ての`a[.]`にセットされただけであるからである。
# 指定したサイズのリストを作成するときに、内包表記を使うこともできる。
# In[79]:
b = [0 for i in range(10)]
b
# これを拡張して、2次元配列を作ることができる。
# In[80]:
b = [[0 for i in range(10)] for j in range(10)]
b
# こちらは、我々が通常期待する2次元配列として振る舞う。
# In[81]:
b[3][5] = 2
b
# ---
#
# [Python早見帳](https://chokkan.github.io/python/) © Copyright 2020-2022 by [岡崎 直観 (Naoaki Okazaki)](https://www.chokkan.org/). この作品はクリエイティブ・コモンズ 表示 - 非営利 - 改変禁止 4.0 国際 ライセンスの下に提供されています。