python3x: list comprehension, list slicing

個人的に魅了されたpythonの特徴の一つでもある`do a lot with little`という考え方をメモ代わりにと。どうやらこの`list comprehension`という手法を使えばより効率的にpythonを操れることが出来ると聞いたので、try it out see if its as kwl as it appears to beです。

#list comprehensionとは

list comprehensionsとは簡単にいえば「何行にも渡ってループ使って変数をいじっていくのが面倒なのでリストの中で全て完結させてしまいましょう」ってこと。一般的なモデルは`[thing for thing in list_of_things]`。

例えばリストの中身の数を2倍にしてくれるという関数を作りたいとする。通常なら:

```
mylist = [1,2,3]

def list_doubler(lst):
    doubled = []
    for num in lst:
        double.append(num*2)
    return doubled
```
関数の中でplace holderとして`doubled = []`としそこに処理を終えた数字を一つづつ入れていくイメージ。つまり`my_doubled_list = list_doubler(lst) #[2,4,6]`として`my_doubled_list`に入れておくことも可能。確かにこういうやりかたでもいいのだが、極端な話わざわざこんなに長いコードを書かなくても実質やりたいことはリストの中身を弄りたいだけなのでリストの中でどうにか完結させられないの？と。ここでlist comprehensionの出番。

##list comprehensionを作ってみる

```
doubled = [thing for thing in list_of_things]
```

```
doubled = [num*2 for num in lst]
# forの前にあるのがdoubledに追加される
```

これで`lst`の中にあるそれぞれの要素を一個ずつ`num`がループして`num*2`してくれる。直感的にリストの中に入っていき`doubled`というリストに保存される。これを一番最初に使った関数と組み合わせると:

```
def list_doubler(lst):
    doubled = [num*2 for num in lst]
    return doubled
```

何行もかけて書いたfor loopがたったの一行に。これはすごい。これに限ったことではないが、この関数に適当なリストを放り込んで返ってきた値（正確には返ってきた値をまとめたリスト）を他の変数に保存する。というかこの場合は普通に:

```
def list_doubler(lst):
    return [num*2 for num in lst]
```

##その他の例

```
>>> squares = []
>>> for x in range(10):
...     squares.append(x**2)
...
>>>squares
[0,1,4,9,16,25,36,49,64,81]
```
ただこの場合は`x`が[side-effct](http://qiita.com/weedslayer/items/9ac165613bc896a1aaa1)として残ってしまう。そこで:

`squares = list(map(lambda x: x**2, range(10)))`

で綺麗さっぱり。もしくはlist comprehensionを使って:

`squares = [x**2 for x in range(10)]`

これのほうが読みやすいし、さっぱりした構造になる。以下はリストの中身を調べて違う要素が入っていたらくっつけてくれる。

```
>>> [(x,y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
```

```
>>> combs = []
>>> for x in [1,2,3]:
        for y in [3,1,4]:
            if x != y:
                combs.append((x,y))
...
...
>>> combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]
```

その他有効活用できそうなものをまとめておいた。

```
>>> vec = [-4, -2, 0, 2, 4]
>>> # create a new list with the values doubled
>>> [x*2 for x in vec]
[-8, -4, 0, 4, 8]
>>> # filter the list to exclude negative numbers
>>> [x for x in vec if x >= 0]
[0, 2, 4]
>>> # apply a function to all the elements
>>> [abs(x) for x in vec]
[4, 2, 0, 2, 4]
>>> # call a method on each element
>>> freshfruit = ['  banana', '  loganberry ', 'passion fruit  ']
>>> [weapon.strip() for weapon in freshfruit]
['banana', 'loganberry', 'passion fruit']
>>> # create a list of 2-tuples like (number, square)
>>> [(x, x**2) for x in range(6)]
[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
>>> # the tuple must be parenthesized, otherwise an error is raised
>>> [x, x**2 for x in range(6)]
  File "<stdin>", line 1, in ?
    [x, x**2 for x in range(6)]
               ^
SyntaxError: invalid syntax
>>> # flatten a list using a listcomp with two 'for'
>>> vec = [[1,2,3], [4,5,6], [7,8,9]]
>>> [num for elem in vec for num in elem]
[1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> from math import pi
>>> [str(round(pi, i)) for i in range(1, 6)]
['3.1', '3.14', '3.142', '3.1416', '3.14159']
```

#List Comprehension with conditionals

一般的な形は`[thing for thing in list if (condition)]`。

例えば以下の様な「5文字以上の単語をリストにいれて返す」という関数があったとする。

```
def long_words(lst):
    words = []
    for word in lst:
        if len(word) > 5:
            words.append(word)
    return words
```

実際にlist comprehensionを適用させると:

```
def long_words(lst):
    return [word for word in lst]
```

```
def long_words(lst):
    return [word for word in list if len(word) > 5]
```

#Nested List Comprehensions

List comprehensionは以下の様なマトリックス表にも適用できる。

```
>> matrix = [
...     [1, 2, 3, 4],
...     [5, 6, 7, 8],
...     [9, 10, 11, 12],
... ]

>>> [[row[i] for row in matrix] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
```

図で表すとこんな感じ:

![Screen Shot 2016-02-05 at 1.20.30 AM.png](https://qiita-image-store.s3.amazonaws.com/0/87666/7e519e1d-af8e-fbb6-676c-b4c9d80ababf.png)

これは以下２つと同じである。

```
>>> transposed = []
>>> for i in range(4):
...     transposed.append([row[i] for row in matrix])
...
>>> transposed
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
```

```
>>> transposed = []
>>> for i in range(4):
...     # the following 3 lines implement the nested listcomp
...     transposed_row = []
...     for row in matrix:
...         transposed_row.append(row[i])
...     transposed.append(transposed_row)
...
>>> transposed
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
```
#List Slicing

参考のためについでにリストスライスもSOから引っ張ってきたのを貼っておく。

```
a[start:end] # items start through end-1
a[start:]    # items start through the rest of the array
a[:end]      # items from the beginning through end-1
a[:]         # a copy of the whole array
a[start:end:step] # start through not past end, by step
```

もう既に知っているかもしれないが
>
_The key point to remember is that the :end value represents the first value that is not in the selected slice. So, the difference beween end and start is the number of elements selected (if step is 1, the default)._

文字をひっくり返したい時とかに役に立つのが以下:

```
a[-1]    # last item in the array
a[-2:]   # last two items in the array
a[:-2]   # everything except the last two items
```

ちなみにリストにないものを指定すると空のリストが返ってくるので注意。

>
_Python is kind to the programmer if there are fewer items than you ask for. For example, if you ask for a[:-2] and a only contains one element, you get an empty list instead of an error. Sometimes you would prefer the error, so you have to be aware that this may happen._

#参考にしたリンク

- [Python Single Line For Loops](http://blog.teamtreehouse.com/python-single-line-loops)
- [2. Built-in Functions](https://docs.python.org/3/library/functions.html#round)
- [5. Data Structures](https://docs.python.org/3/tutorial/datastructures.html)
- [Explain Python's slice notation](http://stackoverflow.com/questions/509211/explain-pythons-slice-notation)