2008年6月14日土曜日

Python の シーケンス型に慣れる

1. 配列の操作

プログラミングは、習うより慣れろ。

そう、避けるべきは黙読で、すべきは朗読です。文書は声に出して読まなければ身につきません。とはいっても、ソースコードまで音読していたらそれはそれで間抜けです。ソースコードは音読ではなく書き写す、すなわち写経するのがいいでしょう。

(本気でやるなら黙読は避けて朗読すべき: Days on the Moon  より)

新しく言語をはじめる場合、最初に確認する書き方の一つに「配列」がある。

配列操作の比較表: Ruby, Python, JavaScript, Perl, C++ - bkブログによると、

プログラムを書いていると、他のプログラミング言語の記憶とごっちゃになって、「配列の後ろに要素を追加するのは push だっけ、 append だっけ」などと混乱することがあります。

これを見ながら、写経することにした。 ( ̄人 ̄)ナムナム

a = [3,2,1]
b = [10,200,30]

# 大きさ
print "len(a) :", len(a)

# 空か?
if len(a) == 0: print "empty"
else:           print "not empty"
# 末尾に追加
a.append(4)
print a

# 挿入
a.insert(2, 100)
print a

# 末尾の要素を取り出す
print a.pop()
print a

# 先頭の要素を取り出す ※
print a.pop(0)
print a

# 別の配列を足す
a.extend(b)
print a

# 特定の値を削除する
a.remove(100)
print a

# 特定の位置にある要素を削除する
del(a[2])
print a
# 特定の値の数をカウントする
print a.count(3)
print a.count(0)

# 特定の要素を含んでいるか調べる
print 3 in a
print 4 in a
# 先頭の要素
print a[0]
# 末尾の要素
print a[-1]

# 特定の区間の要素
print a[1:3]    # 1 ~ 2 まで
# sorted は要素自体を並び換えない
print "sorted(a) :", sorted(a)
print a

# sort は配列自体を並び換える
print a
a.sort()
print a

# 要素を末尾から辿る
for elem  in reversed(a):
    print elem

# reversed は要素自体を操作しない
for x in reversed(a):
    print x
print a

# reverse は要素を自体を逆順にする
a.reverse()
print a
# 配列の要素を、特定の文字列でつなげる
print ', '.join("abc")
print ', '.join(str(x) for x in a)

# 要素を全てクリアする
b = a
a[:] = []
print a
print b

 

uniq

Python で、Ruby の Array#uniq に相当するものは、組み込み関数の set(), list() を使い、 list(set(リスト)) とする。

set([iterable])

集合を表現するset 型オブジェクトを返します。

list([sequence])

sequence の要素と同じ要素をもち、かつ順番も同じなリストを返します。

 

join

Python と Ruby の違うところは、 join が 文字列のメソッドとして定義されていること。

Python はなぜ String#join か - TokuLog 改めChumbyとどきました日記 によると

これ、理由は明解で、「join メソッドは文字列の処理だから」です。

それ以下でもそれ以上でもない。

文字列の処理をするメソッドを、どんなものでも入る Array Class のメソッドとしてはやすなんてとんでもない!

そう考えるのが Pythonista クオリティ

(via Python の join が str のメソッドになっている理由 - odz buffer)

 

2. Python は配列クラスに定義されているメソッドが少ない

こうやって見ると、Ruby は語彙が豊富で何でもありの言語だとすると、Python は正当な表記しか許さない律儀な言語。

「今の若者の言葉の乱れは...」

とでも言いそうな雰囲気を感じる。

認知的経済性から考えると、Python の方が言語としてシンプルで良い。独特の言い回しも許容する、言語の豊かさという視点から見たら、Ruby は使いやすい。

Python には、

「要素をリストの先頭に入れる」関数

に特別な名前がない。insert() による表現の一部になっている。これに対して、Ruby では unshift() という特別な名前が与えられている。

同様に Python では

「リストの先頭の要素を取り出す」関数

は pop() メソッドで代替する。Ruby では shift() という名称が与えられている。

「要素の取得」

においても、Ruby では .first, .last という特別な名前が与えられている。

Ruby は人が使う言葉に沿うようなメソッド名が付けられる傾向があるようように感じる。Python は、「あるもので間に合わせろ」ということか。

String クラスで定義されているメソッドの数を比較からも、そういう雰囲気が伝わってくる。

 

3. 文字列とリスト

Python では、文字列を for ループに投入すると、一文字ずつ処理が行われる。

for c in "hoge":
    print c

for c in u"ほげほげ":
    print c

これに対して、 Ruby では、hoge という一つの文字列として処理される。

for c in "hoge"
  puts c
end

一文字ずつ処理を行いたい場合は、split や scan を使って文字列を分割しなくてはならない。

"hoge".split(//).each do |c|
  puts c
end

"hoge".scan(/./).each do |c|
  puts c
end

# 文字コードが Shift_JIS の場合
"ほげほげ".split(//s).each do |c|
  puts c
end

 

3. シーケンス型

list() 関数に、次のような説明がある。

例えば、list('abc')['a', 'b', 'c'] および list(1, 2, 3)[1, 2, 3] を返します。

これを見て、 Haskell を思い出した。

ふつうのHaskellプログラミング によると、

実は、 Haskell では文字列もリストなのです。文字列は文字のリストとして表現されていて、特別な文字列は存在しません。ですから、リストの処理を覚えてしまえば文字列処理も身についたことになります。 (p36)

Haskell では、 "abc" は、['a', 'b', 'c'] と同じ。なぜなら、String 型は [Char] 型の別名に過ぎないため。

Python のライブラリリファレンスに、組み込み型の説明がされている。

ここに、リスト文字列の上位にある

「シーケンス型」

というものが定義されている。

上記で写経した sorted, reversed メソッドの位置付けも、これを見ると理解できる。

結構、イテレータ型が重要。