重複する要素を取り除いだリストと重複する要素だけを取り出したリスト

リストから重複した要素を取り除く

Python には uniq がないらしい。ああ、なんてこった。
というわけで作ってみた。

def uniq(lis):
    r = []
    for i in lis:
        if i not in r:
            r.append(i)
    return r

a = [1, 1, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9]

print 'Original: ', a
print 'Uniq: ', uniq(a)

もうちょっときれいにならないかな。reduceでできそうな気がする。

^o^ > python uniq.py
Original:  [1, 1, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9]
Uniq:      [1, 2, 3, 4, 5, 6, 7, 8, 9]

リストから重複した要素だけを取り出す

今度は逆に、重複した要素だけを取り出してみる。

def overlup(lis):
    r = []
    def func(x, y):
        if x == y and x not in r:
            r.append(x)
        return y
    reduce(func, lis)
    return r

a = [1, 1, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9]

print 'Original: ', a
print 'Overlup: ', overlup(a)
^o^ > python overlup.py
Original:  [1, 1, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9]
Overlup:   [1, 4, 7]

ネストしたforループから脱出するには例外を使う

タイトルで言いたいことがすんでしまった^^)

forループを途中で脱出するにはbreakを使うけど、breakではひとつのループしか脱出してくれない。たとえば次の例ではbreakしても外側のforループは回り続けて、else節が実行される。

for i in range(5):
    for j in range(5):
        k = i + j
        print k
        if k > 5:
            break

        else:
            print "End of nested loop."
^o^ > python nested_loop.py
0
1
2
3
4
1
2
3
4
5
2
3
4
5
6
3
4
5
6
4
5
6
End of nested loop.

外側のforループもいっぺんに脱出したいときには、例外を使う。たとえばこんなふうに:

class EndLoop(Exception):
    pass

try:
    for i in range(5):
        for j in range(5):
            k = i + j
            print k
            if k > 5:
                raise EndLoop
            else:
                print "End of nested loop."
except EndLoop:
    print "Break in loop."

実行すると、ちゃんと途中でふたつのforループを抜け出していることがわかる。

^o^ > python nested_loop2.py
0
1
2
3
4
1
2
3
4
5
2
3
4
5
6
Break in loop.

挿入ソート

Pythonでやってる人を見かけたので俺も。

import random

def insertion_sort(lis):
    for i in range(1,len(lis)):
        tmp = lis.pop(i)
        for j in range(i):
            if tmp < lis[j]:
                lis.insert(j, tmp)
                break
            else:
                lis.insert(i, tmp)
                return lis


l = range(1,10) * 2
random.shuffle(l)
print "unsorted:", 
 l2 = insertion_sort(l)
print "sorted: ", l2

実行結果:

^o^ > python insertion_sort.py
unsorted: [1, 9, 6, 3, 8, 6, 5, 2, 7, 4, 2, 9, 8, 5, 7, 3, 4, 1]
sorted:   [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9]

Python の for文には else をつけることができるんだ。初めて使った。

リストのappendメソッドはNoneを返す

リスト(数列)の差分のリストがほしかったんだけど、はまったのでメモ。
最初、↓こう書いた。

def difference(lis):
    d = reduce(lambda x, y: (x[0].append(y-x[1]), y), lis, ([],0))
    d[0].pop(0)
    return d[0]

a = range(10)
diff = difference(a)
print diff

2行目のx[0].append(y-x[1])で差分を蓄積していってそれを返している(3行目は余分な初期値との差分があるからそれを取り除いている)。
ところが実行するこうだ。

^o^ > python difference.py
Traceback (most recent call last):
  File "difference.py", line 9, in 
    diff = difference(a)
  File "difference.py", line 3, in difference
    d = reduce(lambda x, y: (x[0].append(y-x[1]), y), lis, ([],0))
  File "difference.py", line 3, in 
    d = reduce(lambda x, y: (x[0].append(y-x[1]), y), lis, ([],0))
AttributeError: 'NoneType' object has no attribute 'append'

‘NoneType’のオブジェクトは’append’なんて属性持ってないと。しばらく悩んだけど、原因はタイトルのとおり。
1回目のappendを呼び出したところでNoneが返ってくるので、2回目の呼び出しでは失敗する。

>>> a = range(5)
>>> a
[0, 1, 2, 3, 4]
>>> type(a.append(5))
<type 'NoneType'>

結局、次のように書き換えたらうまくいったけど、関数がひとつ増えてしまった。

def difference2(lis):
    d = reduce(lambda x, y: (add_list(x[0], y-x[1]), y), lis, ([],0))
    d[0].pop(0)
    return d[0]

def add_list(lis, x):
    lis.append(x)
    return lis

a = range(10)
diff = difference2(a)
print diff
^o^ > python difference2.py
[1, 1, 1, 1, 1, 1, 1, 1, 1]

RubyのArray#pushならselfを返してくれるから素直に書けるんだけどなあ。

zip関数

いまさらだけどPython にも zip関数があるのを知った。というか見逃していた。

>>> a = range(5)
>>> a
[0, 1, 2, 3, 4]
>>> b = ['a', 'b', 'c', 'd', 'e']
>>> zip(a, b)
[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e')]

今まで↓こう書いてたよ。ばかみたい。zipのほうがよっぽどすっきりしている。

>>> map(lambda x, y: (x, y), a, b)
[(0, 'a'), (1, 'b'), (2, 'c'), (3, 'd'), (4, 'e')]

とはいえ、違いもある。
zipは長さを短いほうにあわせる。

>>> c = ['foo', 'bar', 'baz']
>>> zip(a, c)
[(0, 'foo'), (1, 'bar'), (2, 'baz')]

mapだと長いほうにあわせて、足りない部分にはNoneが入る。

>>> map(lambda x, y: (x, y), a, c)
[(0, 'foo'), (1, 'bar'), (2, 'baz'), (3, None), (4, None)]

Luhnアルゴリズムでクレジットカードの番号をチェック:Haskell版

こないだやったLuhnアルゴリズムでのチェックをHaskellでもやってみた。

module Main (main) where

import Data.Char

numbers = [
  "5555555555554444",
  "5105105105105100",
  "4111111111111111",
  "4012888888881881",
  "3530111333300000",
  "3566002020360505",
  "30569309025904",
  "38520000023237",
  "378282246310005",
  "371449635398431",
  "378734493671000",
  "6011111111111117",
  "6011000990139424"
]

checkNumber num = sum' `mod` 10 == 0
  where
    sum' = sum $ zipWith add' (cycle [1,2]) (reverse num)
    add' x y = let z = x * digitToInt y
    in
    if z > 9 then z - 9 else z

main = mapM_ (print . checkNumber) numbers

実行結果:

^o^ > runhaskell checkCardNumber.hs
True
True
True
True
True
True
True
True
True
True
True
True
True