Python2で競技プログラミングする時に知っておきたいtips(制御構造編)


[Python2で競技プログラミングする時に知っておきたいtips](http://qiita.com/lethe2211/items/b91cc9e2b355ad32f1e6)の，制御構造についての部分を分割しました．

Pythonのバージョンは2.7.5（Python3では，入出力などの仕様が大きく異なるので，他の記事を参照することをおすすめします）．

## 条件分岐

### 条件式に変数を渡す

個人的にはあまり好きではないが，コード量削減のために，

```python
while 1:
    a = input()
    if not a: 
        break
    # ...   
```

みたいに，if文，while文などの条件に変数を渡すことが多い気がする．

[PythonでIf文に変数を入れたときの結果 - LIFE WITH PYTHON](http://www.lifewithpython.com/2013/02/python-if-statement-data-type.html)

| 型 | 結果 |
|:---------:|------------|
| int       | 0ならFalse，それ以外ならTrue |
| float     | 0.0ならFalse，それ以外ならTrue |
| str       | ''(空文字)ならFalse，それ以外ならTrue |
| list      | []ならFalse，それ以外ならTrue |
| tuple     | ()ならFalse，それ以外ならTrue |
| dict      | {}ならFalse，それ以外ならTrue |
| set       | set()ならFalse，それ以外ならTrue |
| None      | False |

### 処理を途中で終了したい

入力の終わりを表す記号や，コーナーケースが入力された場合など，途中でスクリプトの実行を終了してしまった方が楽な場合がある．
このような場合は，`quit()`または`sys.exit()`を使うとよい．

```python
a = input()
if a == 0:
    quit()
# ...
```

```python
import sys

a = input()
if a == 0:
    sys.exit()
# ...  
```

## 繰り返し

### range()とxrange()

[2. 組み込み関数 — Python 2.7ja1 documentation](http://docs.python.jp/2/library/functions.html)

Pythonで「○○をn回行う」と言った単純な繰り返しを実装する場合は，

```python
n = 5

for i in range(n):
  print i
# 0
# 1
# 2
# 3
# 4
```

または，

```python
n = 5

for i in xrange(n):
  print i
# 0
# 1
# 2
# 3
# 4
```

のように書くのが一般的．

ここで，`range(n)`，`xrange(n)`は，それぞれ，0からnまでの整数を昇順に並べたlistオブジェクト，xrangeオブジェクトを返す．
xrangeオブジェクトは，遅延評価を用いることで，値が必要とされるときに取り出すことの出来るオブジェクト．
nが非常に多くなる場合，`range(n)`ではリストを生成してからループを行うため，メモリ使用量が大きくなってしまうが，`xrange(n)`ではそれが避けられる．
また，速度についても，一部の場合を除いて`xrange()`の方が高速．
[python - Should you always favor xrange() over range()? - Stack Overflow]
(http://stackoverflow.com/questions/135041/should-you-always-favor-xrange-over-range)

また，(x)rangeでは，第2，第3引数を用いたり，`reversed()`を用いることで，いろいろな範囲の表現ができる．

```python
# range(start, stop)で，startからstop-1までの整数すべてを昇順に並べたリストを返す
print range(2, 6) # [2, 3, 4, 5]

# range(start, stop, step)で，startからstepおきに値を増やしていって得られる整数の内，stop-1までのすべてのものを昇順に並べたリストを返す
print range(5, 30, 5) # [5, 10, 15, 20, 25]

# reversed()でリストを逆順にできる
for i in reversed(range(5)):
    print i
# 4
# 3
# 2
# 1
# 0
```

### for文での変数の展開

for文で，

```python
l = [1, 2, 3, 4, 5]
for e in l:
    print e
```

```:output
1
2
3
4
5
```

のように，リストの各要素を展開できるが，これは，

```python
l = [(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd')]
for i, e in l:
    print '#{0}: #{1}'.format(i, e)
```

```:output
0: a
1: b
2: c
3: d
```

のようにもできる．

### enumerate

forループを回す際，リストのインデックスと値が同時に取得できるといい場合がある．

`enumerate()`は，引数のリストに対して，インデックスとその値を持つイテレータを返す．

```python
l = ['a', 'b', 'c', 'd']
for elem in enumerate(l):
    print elem
# (0, 'a')
# (1, 'b')
# (2, 'c')
# (3, 'd')

# 上のコードは下記のコードと等価
for i in range(len(l)):
    print (i, l[i])
```

### for-else，while-else文

Pythonでは，for文，while文にelse節を加えることができる．
この際，else節は，ループ終了までに一度も内部でbreakされなかった場合のみ呼び出される．

```python
a = [1, 2, 3, 4, 5]
b = [1, 2, 4, 5]
x = 3

def find_val(l, x):
    for i in range(len(l)):
        if l[i] == x:
            print i
            break
    else:
        print 'Not found...'

# 上のfind_val()と等価なコード
def find_val2(l, x):
    flag = False
    for i in range(len(l)):
        if l[i] == x:
            print i
            flag = True
            break
    if not flag:
        print 'Not found...'

find_val(a, x) # 2
find_val(b, x) # Not found...
```

「xを探せ．見つからなかった時は'hoge'を出力しろ」みたいな設問の際，フラグを持つ必要が無いため，非常に書きやすくて便利．
ただし，ループが多重になる場合はかえって見通しが悪くなるため，使わない方がよい．