2008年9月30日火曜日

Python の map, filter, reduce とリスト内包表記

1. リストを操作する関数で重要なのはどれ?

リスト内包表記は慣れたら使いやすい

Python のリスト内包表記に出会って 4 ヶ月が経った。

Python のリスト内包表記」を読みなおしてみると、

… 同じく数値のリストから、特定の条件に合う要素を抽出する。

print [x for x in [1,2,3,4,5] if x > 3]

これまた読みにくい。 (@_@;)

と書いていたけれど、今では「シンプルで読みやすく、また書きやすい」と思える。慣れとは恐ろしい。 ^^;

(リスト内包表記がネストしてたりすると、すぐに理解出来ないけれど。)

 

Ruby の Enumerable モジュールにはたくさんのメソッドが定義されている

Java しか知らなかった頃、Ruby の 配列に定義されているメソッドを見て、「便利なメソッドがたくさんあるなぁ」と思った。Ruby の配列は、Enumerable モジュールをインクルードしており、そこにいくつかメソッドが定義されている。

配列だけではなく、自分でクラスを定義した場合でも、 Mix-in の機能により、each を定義するだけで簡単にパワーアップ。

しかし、 Enumerable モジュールに定義されている、20個程度のメソッドを見ても、どれが重要なのかわからない。 (@_@;) 

sort メソッド以外では、

  • each
  • each_with_index

ばかりに目が行ってしまい、

  • map
  • select
  • inject

なんて便利なんだけど脇役だと思ってた。

 

Haskell, Python でも同じような関数が定義されている

Haskell, Python を触るようになって、上記の印象が変わった。両方とも、リスト内包表記が特別に用意されている言語。

例えば、Haskell の Prelude の目次を見ると、List operations として真っ先に map が定義されている。リストの結合(++) に続いて filter , 少し間を空けて、Reducing lists (folds) がある。

あ~、なるほど、これらが重要な関数なんだと理解できた。

map, filter, reduce 関数が重要だと認識しても、昔からの慣習の力はすごい。 (+_+) リストの要素に関数を適用することを考える場合、for ループで要素を走査するイメージのしやすは、コードを書くときのの強力な引力として働く。

for ループを書くこと自体、別に悪くない。しかし、面倒くさがりやなので、お決まりのコードはなるべく短く書きたい。そこで、for ループとの対応を明確にして、map, filter, reduce 関数の使い方を練習しておく。できれば、リスト操作を考えるとき、ループをイメージするのを飛びこして、 map, filter, reduce へと思考がダイレクトに反応することを願って。 o(^^)o

 

2. リスト全体に対する計算 : reduce

合計

リストを走査して合計を求める。

L = [1,2,3,4,5]

result = 0
for e in L:
    result += e
print result

以後、リスト L を使う。

reduce を使えば、

print reduce(lambda a,b: a+b, L)

合計に関しては特別に、

print sum(L)

 

総乗
result = L[0]
for e in L[1:]:
    result *= e
print result

reduce を使えば、

print reduce(lambda a,b: a*b, L)

 

リストの要素全体に渡って、何らかの演算をした結果を積立てていきたいときは reduce を思い出すこと。

(1+2)+3)+4)+5)

 

3. リストの要素に演算を適用 : map

例えば、要素を 2 倍する

result = []
for e in L:
    result += [e*2]
print result

map, リスト内包表記を使うと、それぞれ、

print map(lambda x: x*2, L)
print [x*2 for x in L]

reduce を使うなら、

print reduce(lambda a,b: a+[b*2], L, [])

空のリストを初期値として渡し、要素に対して演算を適用した後はリストにするところがポイント。

 

4. リストの要素を抽出 : filter

例えば、 3 より大きい要素を得る場合、

result = []
for e in L:
    if e > 3:
        result += [e]
print result
filter, リスト内包表記を使うなら、 それぞれ、
print filter(lambda x: x>3, L)
print [x for x in L if x > 3]

reduce を使うなら、

print reduce(lambda a,b: a+[b] if b > 3 else [], L, [])

空のリスト[] をリストに足しても変わらないことがポイント。

 

5. リストの要素を抽出して演算を適用 : filter と map

例えば、3 より大きい要素を得て 2 倍する

result = []
for e in L:
    if e > 3:
        result += [e*2]
print result

filter と map 、リスト内包表記を使うと、それぞれ

print map(lambda x: x*2, filter(lambda x: x > 3, L))
print [x*2 for x in L if x > 3]

reduce を使うなら、

print reduce(lambda a,b: a+[b*2] if b > 3 else [], L, [])

やはりリスト内包表記はシンプルでいい。 ^^

2008年9月29日月曜日

Python で順列を生成

1. 順列の意味

順列 – Wikipedia によると、

組合せ数学における順列(じゅんれつ、permutation)は、あるひとつの集合から要素を選び出して、順番に意味を持たせて並べる (ordering) ときの、その並び(ordered list, sequence; 有限列)のことである。

うーん、ややこしそう。 (+_+)

数学苦手。どうやって生成するんだろう。

 

2. アルゴリズム

順列の生成とList内包表記 - 趣味的にっき によると、

与えられたリストから要素を1つ取り出して、残りの要素から再帰的に順列を求めて、それらを結合するアルゴリズムです。 (…)

まずHaskellの場合。 (…)

perms :: Eq a => [a] -> [[a]]
perms [] = [[]]
perms xs = [ h : t | h <- xs, t <- perms (xs \\ [h]) ]

えーーー、こんな簡単なアルゴリズムとは。 (@_@;)

しかし、自分の脳みそではすぐにイメージできない。

 

Haskell の文法

ゆっくり考えてみよう。

リスト内包表記に書かれていることを、落ちついて眺めてみる。

まず、基本的なこととして (\\) はリストからリストを引く操作。

The \\ function is list difference ((non-associative).

(Data.List より)

リスト内包表記において、リストを生成する元 (生成器) が二つなので、その要素が組み合わされたものが生成される。

リスト内包表記[ exp | qual1 , ... , qualn ]の exp にリストを生成するための (:) が使われている。(:) はリストを生成するためのデータコンストラクタ。

The Haskell 98 Library Report: List Utilities によると、

-- []((:), []), -- This is built-in syntax

これを exp (式)として利用できるのは、 の定義の中で、

gcon (general constructor)

とあるからなのか。

h には、与えられたリストから要素が一つずつ取り出される。 t には、与えられたリストから、その h を取り除いた残りの要素から一つずつ取り出される。これが組み合わされてリストが生成される。

ふぅ (+_+)、やっとこれで、やっていることが理解できた。

しかし、なぜこれで順列を生成することができるのだろうか?

 

3. 順列における再帰構造

やはり、絵を描いて理解するしかない。

要素が一つの順列、二つの順列、三つの順列…。もうこの辺りで面倒なので、四つの順列は一部のみを描いた。

三つの順列を描いた後に、じっと眺めてから、やっと気がついた。 (@_@) 先ほどのアルゴリズに書かれていたことが、絵に描かれている。

例えば、三つの順列を描いた一番上の要素、1 - 2 – 3 と 1 – 3 – 2 に注目。先頭の「1」を取り除いた部分だけを見ると、「2, 3」の順列と同じ。同じような視点で 1, 2 の順列を見ると、同じく先頭の要素を取り除けば、残りの「要素一つの順列」と見ることができる。 「おぉ~、これは (@_@)」 と思い、四つの順列も一部を描いたので同じようにして見ると、先頭を取り除いた順列が、残りの要素の順列からなることがわかる。

permutation

 

4. Python で実装

Python ではリスト内包表記が使えるので、 Haskell のコードを真似することができる。

def permutations(L):
    if L == []:
        return [[]]
    else:
        return [[h]+t for i,h in enumerate(L)
                      for t   in permutations(L[:i]+L[i+1:])]

はじめ、リストから要素を一つ取り出し、その要素を取り除いたリストを得る方法がはじめよくわからず。 (+_+)  enumerate() を使って対象のリストをインデックスと共に取り出し、そのインデックスを利用して同リストからその要素だけ削除した。

上記のようにリスト内包表記で書く方法がわからなかったときは、次のように書いていた。

def permutations(L):
    if L == []:
        return [[]]
    elif len(L) == 1:
        return [L]
    elif len(L) >= 2:
        result = []
        for i in range(1,len(L)+1):
            result.extend([L[i-1]] + x
                for x in permutations(L[0:i-1]+L[i:]))
        return result

うーん、なんかややこしい。 (+_+)

 

5. ジェネレータを使った実装

には、ジェネレータを使った順列、組合せなどが定義されたモジュールがある。

実際に使うときはこれを使うのがよさげ。

 

6. Haskell のコードを一部修正

ところで、上記 Haskell のコードにおいて、perms [1, 2, 3] と perms [1, 2, 1] の結果を並べて表示させると、

[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
[[1,2,1],[1,1,2],[2,1,1],[2,1,1],[1,2,1],[1,1,2]]

あれ? 上記二つの結果は、3 を 1 に変更しただけなので、1 と 2 は全て対応していないとおかしい。調べてみたら、The Haskell 98 Library Report: List Utilities によると、

(\\) はリストの差(結合性はない)である。 xs \\ ys の結果は、xs のなかから ys の各要素の最初に出現したものを取り除いた残りである。 (太字は引用者による)

なるほど、(\\) は、最初に出現するものを取り除くから、[1, 2, 1]  \\ [1] は 2 回行われるけれど、両方とも先頭の 1 が消されて [2, 1] が返ってしまう。そのために上記のような結果になったようだ。

ということは、Python の enumerate() のような関数を使って、インデックスを用いて要素を削除すればいいわけか。…と思って、Data.List, List operations 辺りをざっと見ても見つけられなかったので、要素から取り出すときに, zip でインデックスに相当するリストとくっつけて取り出し、そこから取り出したインデックスの値で要素指定して削除する関数 takeWithout を定義した。

もっと楽にできる方法ってないのかな?

import Data.List ((\\))

perms    :: Eq a => [a] -> [[a]]
perms [] = [[]]
perms xs = [ h : t | (i,h) <- zip [0..] xs, 
                         t <- perms (takeWithout i xs) ]

takeWithout      :: Int -> [a] -> [a]
takeWithout n xs = take n xs ++ drop (n+1) xs

main = print $ perms [1,2,1]

結果は、

[[1,2,1],[1,1,2],[2,1,1],[2,1,1],[1,1,2],[1,2,1]]

追記(2010.2.20) : zip でインデックスに相当するリストとくっつけるとき、以下のように書いていた。 (便宜的に関数名を zip’ とつける)

zip' xs = zip [0..(length xs)-1] xs

しかし、遅延評価により無限リストを使って以下のように書いた方がシンプル。

zip' = zip [0..]

「煮詰る」って誤用なの?

1. 「誤用」というのが、そもそも言葉の本質からして誤用

言葉の誤用としてよく挙げられる 「煮詰る」 という言葉。

例えば、「これ以上考えても、どうしようもない」という意味のつもりで、次のように言ったとする。

「(プログラムの) コードを書いていたら、煮詰まったので気分転換しよう」

この言葉遣いに全く違和感を感じないし、そのような状況のとき、この言葉が自然と頭に思い浮かぶ。

しかし、Yahoo!辞書 - に‐つま・る【煮詰(ま)る】 によると、

討議・検討が十分になされて、結論が出る段階に近づく。

とある。自分が使っている言葉の感覚とは、真逆のことが書かれている。

上記に続けて、次のような解説がある。

近頃では、若者に限らず、「煮詰まってしまっていい考えが浮かばない」のように「行き詰まる」の意味で使われることが多くなっている。本来は誤用2の意は1900年代後半に始まるようである。「行き詰まる」の意は1950年ころの使用例があるが、広まったのは2000年ころからか。

ここで気になるのは「本来は誤用」と書かれている点。「誤った用い方」というからには、「正しい使い方」があることを前提としている。

恐らく上記で用いられている「誤用」という言葉は、次のことを意味しているに過ぎない。

「ある特定の時代における、特定の集団において、多くの人が使っている使い方とは違う」

100年単位のマクロな視点から見れば、日常的な言葉が担っている意味など、吹けば飛ぶような将棋の駒。同じ言葉でも、それが意味するところは変化する。それに対して、「本来」とつけるのはナンセンス極まりない。

ただし、時間的にも空間的にも有限な自分が言葉を使ってコミュニケートできるのは、まさに「ある特定の時代における、特定の集団において」なので、これを「本来」と言いたい心情も分かる。

 

2. 言語というツ-ルの特徴は、コンテキストによって決まること

サピア の 「言語―ことばの研究序説」 によると、

… 言語要素は何よりもまず、記号である。 … 幾千もの異なる経験を包含し、なおその上に、幾千もの経験を進んで取り入れようとする、思考の便利なカプセルに対する記号なのだ。 (p.29)

言語の本質的な事実は、むしろ、概念を分類し、形式的なパタン化をし、関係づけることにある。(p.42)

「言葉の意味は使用によって定まる」と、昔、友人から聞いたことがある。まさにその通り。

言葉は「製品」のように想定される使われ方があるのではなく、使い方の決まっていないツール。他人が使っているのを見て使用されるコンテクストを覚え、自分が使うときの指針とする。

ツールの使い方を決めるのは自分自身であり、他人によって承認される。

 

3. 「煮詰る」 と 「煮 + 詰る」 は違う

さて、最初に挙げた例に戻る。

「(プログラムの)コードを書いていたら、煮詰まったので気分転換しよう」

なぜこの言葉使い方が、自分にはしっくりくるのだろう?「本来」誤用であるにもかかわらず。

 

a. 「行き詰まる」では、しっくりこない

先ほどの辞書の解説に、煮詰まるは 「行き詰まる」 の間違いであると書かれている。しかし、次のように「行き詰まる」を「煮詰まる」で置き換えても、自分にはしっくりこない。

「(プログラムの)コードを書いていたら、行き詰まったので気分転換しよう」

自分がイメージするする「行き詰まる」の意味は、進んでいる先の道が行き止まりで、前に進もうにも壁が阻んでいるというもの。進もうにも進めない場面でしか使わない。行き詰まった先には 「諦め」 が予定されている。「気分転換」など、悠長にしている場合ではない。

これに対して、自分が使う 場合の「煮詰る」 が意味しているものは、必ずしも行き止まりではないし、その後「諦める」 かどうかは状況による。自分が伝えたかった言葉の意味は、

「気分転換したら、別の方法で再チャレンジしてみよう」

ということ。 「行き詰まった」 という言い方より、 「詰り方」 に希望が持てるつもりで言葉を用いている。

 

b. 自分の「煮詰まる」のイメージの由来

では、なぜそのような言葉のイメージが生まれたのだろうか?

元々、「煮詰まる」とは、「煮えて水分がなくなる」煮物より完成を暗示する。これは自分が使う言葉に対するイメージとは全く異なる。

自分の場合、ここでの「煮る」の意味は、

  1. 煮る「対象」があり、
  2. これを調理する

ことを指す。そして、「詰まる」は、「煮る」の意味から独立していて、

「考えに詰まる」

という意味と関連している。

つまり、この言葉を使うとき、

  1. 煮る対象が「思考」に相当し、
  2. それらが煮たことにより、形が崩れぐちゃぐちゃになる

というイメージと結びく。よって、

煮る + 詰まる

の二つの語が合成されたものと認識している。決して「煮詰る」の 1 語に由来した意味を意図しているのではない。

表面上は「煮詰る」と同じように見えるけれど、自分のイメージしている言葉は、「本来の意味」と言われる「煮詰る」とは、全然意味合いが違う。

 

4. 最後に

新しい概念の誕生は、必ず、古い言語材料の多少のこじつけた、または拡張した用法によって予示されている。

(言語―ことばの研究序説, p.35 より)

 

5. 関連サイト

2008年9月28日日曜日

Python でリスト内のリストを連結 - Haskell の concat 関数を真似る

1. ネストしたリストの要素を結合したい

Python で、リスト内のリストを結合したい。

[[1,2,3],[4]] → [1,2,3,4]

Haskell の concat 関数 に相当する関数を探したが、見つからなかった。

Prelude> concat [[1,2,3],[4]]
[1,2,3,4]

自分で書くしかないのかな ?

 

2. Haskell の concat 関数は、型に注意

最初に、Haskell の concat 関数の動作を確認する。

Prelude によると、concat 関数の型は、

concat :: [[a]] -> [a]

いくつかの例を試す。

test1a = [[1,2,3],[4]]
test1b = [[1,2,3],4]

test2a = [[[1,2,3]],[[4]]]
test2b = [[[1,2,3]],[4]]

main = print $ concat test1b

実行したら、エラーが出た。 (+_+)

concattest.hs:2:18:
    No instance for (Num [t1])
      arising from the literal `4' at concattest.hs:2:18
    Possible fix: add an instance declaration for (Num [t1])
    In the expression: 4
    In the expression: [[1, 2, 3], 4]
    In the definition of `test1b': test1b = [[1, 2, ....], 4]

concattest.hs:5:21:
    No instance for (Num [t])
      arising from the literal `4' at concattest.hs:5:21
    Possible fix: add an instance declaration for (Num [t])
    In the expression: 4
    In the expression: [4]
    In the expression: [[[1, 2, ....]], [4]]

Haskell におけるリストの要素は、同じ型でなければならない。

[a]

「単一の数値」と「数値のリスト」は、型が異なる。「リスト」と「リストのリスト」も、型が異なる。

基本的なところを押さえてなかった。 パタッ(o_ _)o~†

 

3. Python で concat 関数を実装する

Haskell に準じて、

[[1, 2, 3], 4]

というリストは、対象にしないことにする。

「リストの中にあるリスト」

を結合する関数を定義する。

 

for ループ、リスト内包表記を使う場合

まずは、単純に for ループを使って実装する。

result = []
for es in [[1,2,3],[4]]:
    for e in es:
        result.append(e)

同じものをリスト内包表記で書く。

result = []
[[result.append(e) for e in es] for es in [[1,2,3],[4]]]

そうえいば、ループを二重に回す必要はなかった。 ^^; リスト内の要素を結合すると考えれば良い。

result = []
for e in [[1,2,3],[4]]:
    result += e

リスト内包表記の場合、上記のように代入文を使えないので extend を使う。

result = []
[(lambda x: result.extend(x))(e) for e in [[1,2,3],[4]]]

 

reduce 関数を使う

reduce 関数を使えばシンプルに書ける。

reduce(lambda a,b: a+b, [[1,2,3],[4]],[])

 

再帰的な定義

再帰で考える場合は、

  1. 空のリスト → 空
  2. 要素が 1 つのリスト → 要素を取り出す
  3. 要素が 2 つ以上 → 先頭の要素を取り出したものと、それより後ろの要素に再帰的に適用した結果を結合
def rec_concat(L):
    if L == []      : return []
    elif len(L) == 1: return L[0]
    else            : return L[0] + rec_concat(L[1:])

追記(2008.9.28) : 上記で書いた、リストの要素数が 1 つのときの処理は冗長だった。 (+_+) 要素が1つの場合は、要素が 2 つ以上のときの処理でまかなえる。要素が 1 つのときは、一つの要素の後ろに空の要素があると考えれば良い。

def rec_concat(L):
    if L == [] : return []
    else       : return L[0] + rec_concat(L[1:])

リストの要素が一つのとき、L[1:] は [] が返される。よって、空の検査をしている条件が適用される。

 

Haskell の concat の実装と比較する

Haskell の concat の実装は、Haskell Code by HsColour によると、

concat :: [[a]] -> [a]
concat = foldr (++) []

foldr 関数は、Python の reduce に相当すると見なせば良い。

 

itertools モジュールを利用する

追記(2008.9.28) : 6.5.3 Recipesitertools モジュールにおける chain 関数を使った例が書かれていた。このモジュールを使うなら、

from itertools import chain
def flatten(listOfLists):
    return list(chain(*listOfLists))

print flatten([[1,2,3],[4]])

変数 listOfLists を可変引数 *listOfLists に渡すことによって、リストの要素に分割されるようだ。

(cf. Python の可変引数)

 

4. Ruby の flatten の動作を再確認

Ruby には、どれだけ要素がネストしていようと、真っ平らにしてまうメソッドが Array クラスにある。

p [[1,2,3],4,[[[[[[[[[[5,[6,7]]]]]]]]]]]].flatten

結果、

[1, 2, 3, 4, 5, 6, 7]

Python で flatten - ネストしたリストをフラットにする」につづく…

Ruby, Python, Haskell の条件式

Ruby の if は値を返す

以前、 Java しか知らなかった頃、Ruby で if が値を返すと読んだときは驚いた。 (@_@;)

if 式は、条件が成立した節(あるいは else 節)の最後に評価した式の結果を返します。else 節がなくいずれの条件も成り立たなければ nil を返します。

(制御構造 - Rubyリファレンスマニュアル より)

例えば、「テストで 80 点以上だったら `A’, 79 ~ 60 点では `B’, それ以下は `C’ 」としたい場合、

x = 30

result = if x >= 80 then 'A' elsif x >= 60 then 'B' else 'C' end
p result 

p x >= 80 ? 'A' : x >= 60 ? 'B' : 'C'

上記のように 条件演算子 を用いて書くこともできる。

テストの点数の配列に対して適用する場合には、

p [50, 100, 80, 60].map{|x| if x >= 80 then 'A' elsif x >= 60 then 'B' else 'C' end}

 

Python の条件式で値を返す

1 PEP 308: Conditional Expressions によると、Python では 2.5 から値を返す条件式を書くことができる。

x = 30
print 'A' if x >= 80 else 'B'

条件を追加するには、

print 'A' if x >=80 else 'B' if x >= 60 else 'C'

elif は使わないようだ。

はじめて見たときは、見にくかったので括弧を付けてみると、

print 'A' if a >=80 else ('B' if a >= 60 else 'C')

ん~ (@_@;) なくてもいいかなぁ。

 

リスト内包表記 で 条件式 を使う

例えば、テストの結果のリストがあった場合、次のように lambda と リスト内包表記 を使うことができる。

print [(lambda x: 'A' if x >=80 else 'B' if x >= 60 else 'C')(e)
        for e in [50, 100, 80, 60]]

なるほど、 (lambda)() という形式で呼出せばいいのか。

 

Haskell の場合はもちろん値を返す

Haskell では 3.6 条件式 if  または ガード を使って、

result x = if x >= 80 then "A" else if x >= 60 then "B" else "C"

result2 x
    | x >= 80 = "A"
    | x >= 60 = "B"
    | otherwise = "C"

main = putStrLn $ result 30

あ~、そうか elsif みたいなのは必要ないわけか。

追記 (2010.1.5) : Haskell 98 Report: 式 の「3.6 条件式」 によると、

以下の同一性が保存される

if e1 then e2 else e3 = case e1 of { True -> e2 ; False -> e3 }

だから、 case 式で同等な式を記述できる。

Inkscape でオブジェクトの枠の色を素早く変更する

1. フィルとストローク

Inkscape で、オブジェクトの枠の色を変更したい。

オブジェクトの「内部」と「周囲の枠」は、それぞれ次のように呼ばれる。

  • フィル (Fill) : 内部
  • ストローク (Stroke) : 周囲の枠

 

2. オブジェクトの色を変更

オブジェクトの内部の色を変更する

作成したオブジェクトの「中身」の色を素早く変更するには、ウィンドウ下部にあるタイル状の色をクリックする。

080928-002

 

オブジェクトの周囲の枠の色を変更する

同じ方法でオブジェクトの「枠」を変更するには、上記と同じ場所を Shift キーを押しながらクリックする。

または、同じ場所を 右クリック > ストロークに設定(Set stroke) を選択する。

080928-004

 

3. 色を細かく調整をする

色の細かい調整をするには、メニューより

  • Object > Fill and Stroke… (Shift + Ctrl + F)

を選択して、調整するためのペインを表示する。ショートカットキーは、Fill の 「F」で覚えておく。

または、ウィンドウ左下部にある Fill, Stroke に表示されている色の部分をクリックしても同じものが表示される。

080928-003

2008年9月27日土曜日

Python で正確な小数の計算 (3) – float, Decimal の有効桁数

1. float, Decimal の有効桁数を確認しておく

これまでに 「float よりも精度の高い Decimal」 、「Decimal を文字列として出力するときの str() と repr() の違い」について見てきた。

今回は、float と Decimal 有効桁数、str() と repr() の有効桁数について試しておく。こういうのは、一度自分で動作を確認しておかないと気持ちが悪い。 (+_+)

 

2. 事前準備

毎回 print 文で、式と結果を書くのは面倒。予め、与えられた式の文字列を受けとると、str() と repr() の結果を表示する関数を作成しておいた。

def p(E):
    print E, " = ", str(eval(E)), " : ", repr(eval(E))

print 文は、str() を呼出していたと思うけれど、念のため…。

 

3. 有効桁数を確認する

str(), repr() の丸め

最初に、思い出しておくべき、基本事項は、

残念なことに、ほとんどの小数は 2 進法の分数として正確に表わすことができません。その結果、一般に、入力した 10 進の浮動小数点数は、 2 進法の浮動小数点数で近似された後、実際にマシンに記憶されます。

(B. 浮動小数点演算、その問題と制限 より)

float で小数は近似値であるということ。

str() と repr() についての違いを確認しておく。、

str() 関数は有効数字 12 桁しか生成しません

repr(float) は真の 10 進値を有効数字 17 桁で丸め

B. 浮動小数点演算、その問題と制限 より)

「真の」値を丸めているということを忘れずに。

ハードウェアが浮動小数点数を記憶するのに用いているビット数がマシンによって異なり、Python は単にマシンに 2 進で記憶されている、真の 10 進の値を近似した値を、されに 10 進で近似して出力する 

(同上より)

念のため原文で確認。

because the number of bits used by the hardware to store floating-point values can vary across machines, and Python only prints a decimal approximation to the true decimal value of the binary approximation stored by the machine.

(B. Floating Point Arithmetic: Issues and Limitations より、太字は引用者による。)

以上を整理して、図にすると、

080927-002

 

float

境界を試してみる。

p('0.111111111019 ')           # 12 桁 ここまで
p('0.1111111110119')           # 13 桁

p('0.11111111101111119 ')           # 17 桁 ここまで
p('0.111111111011111119')           # 18 桁

結果は、

0.111111111019   =  0.111111111019  :  0.11111111101899999
0.1111111110119  =  0.111111111012  :  0.11111111101189999
0.11111111101111119   =  0.111111111011  :  0.11111111101111119
0.111111111011111119  =  0.111111111011  :  0.11111111101111112

なぜ 17 桁なのかと言うと、

repr(float) が有効数字 17桁 の値を生成するのは、この値が (ほとんどのマシン上で) 、全ての有限の浮動小数点数 x について eval(repr(x)) == x が成り立つのに十分で、かつ有効数字 16 桁に丸めると成り立たないからです。

これは 2 進法の浮動小数点の性質です: Python のバグでも、ソースコードのバグでもなく、浮動小数点演算を扱えるハードウェア上の、すべての言語で同じ類の現象が発生します

(同上より、太字は引用者による)

 

Decimal

Decimal は文字列として与えると、演算が行われるまでそのまま保持しているようなので  1 を掛けて結果を見た。

p('Decimal("0.1111111110111111111011111119")  * 1')  # 28 桁 ここまで
p('Decimal("0.11111111101111111110111111119") * 1')  # 29 桁

結果は、

Decimal("0.1111111110111111111011111119")  * 1  =  0.1111111110111111111011111119  :  Decimal("0.1111111110111111111011111119")
Decimal("0.11111111101111111110111111119") * 1  =  0.1111111110111111111011111112  :  Decimal("0.1111111110111111111011111112")

 

4. 今回ハマったところ

1 / 3 * 3

を float と Decimal で試してみると、

p('1. / 3. * 3.')
p('1. / 3. * 3. == 1')

p('Decimal(1) / Decimal(3) * Decimal(3)')
p('Decimal(1) / Decimal(3) * Decimal(3) == 1')

結果は、

1. / 3. * 3.  =  1.0  :  1.0
1. / 3. * 3. == 1  =  True  :  True
Decimal(1) / Decimal(3) * Decimal(3)  =  0.9999999999999999999999999999  :  Decimal("0.9999999999999999999999999999")
Decimal(1) / Decimal(3) * Decimal(3) == 1  =  False  :  False

表示にイコール  = があるので見にくいけれど… ^^;

上記では、一見正確でないはずの float の方が正確な答えを出していて、精度が高いと思っていた Decimal が間違っているように見える。なぜだろう。 (@_@;) ?

 

まずは、「数」について、

整数は0と0から+1または、-1ずつしていき得られる数のこと。負の整数、0、正の整数があります。→-3,0,7など
自然数は整数のうち、正の整数のものをさします。→1,7,13など
有理数は分数で現されるものをさします。いい替えると、整数および有限小数と無限循環小数です。→0.2,1/3,0.125など
無理数は分数で現されない小数です。つまり、循環しない無限小数のことです。→円周率(3.14…),√2(1.414…)
実数は虚数でない数。つまり無理数、有理数、整数、自然数をすべて含めた数です。

(実数、無理数、有理数、整数、自然数の関係を分かりやすく教えてください!! それ... - Yahoo!知恵袋 より、太字は引用者による)

念のためもう一つの説明を、

自然数【1、2、・・・】 ⇒ ものの個数など、数の基礎。

整数【・・・、-2、-1、0、1、2、・・・】 ⇒ マイナスという概念とゼロという概念が加わったもの。

有理数【p/q】 ⇒ 割り算(分数)という概念によるもの。2/3、-3/4など。

無理数 ⇒ 有理数では表せないもの。3.14159…など。

実数 ⇒ 上記の有理数(自然数や整数を含む)・無理数を合体したもの。

(数学Aの事で質問です 素数、自然数、有理数、無理数、実数、整数の違いがごちゃ... - Yahoo!知恵袋 より、太字は引用者による)

つまり、 1 / 3 = 0.3333333333… とずっとつづく無限小数。

 

float による計算

では、float による計算の過程を追うと、

1. / 3.  =  0.333333333333  :  0.33333333333333331

上記のように、str() による出力は13 桁目が丸められ、 repr() の出力は 18 桁目が丸められる。その後ろがどうなっているかは表示に表われない。

次に、正確ではないけれど (真の値ではないという意味) 、上記の答えに 2 を掛けたのと同じ状態にしてやると、

0.33333333333333331 + 0.33333333333333331  =  0.666666666667  :  0.66666666666666663

str() の出力は、13 桁目が丸められて 12 桁目が 7 となる。 repr() の出力は、最後が 31 + 31 で 62 となると思いきや 63 。これは、31 以降にあると考えらえる真の値の数が丸められて 3 になったということなのかな。

3 を掛けたときの真の値がどのようなものかわからないけれど、少しずつ 17 桁の値を変化させて見てみると、

0.99999999999999990  =  1.0  :  0.99999999999999989
0.99999999999999991  =  1.0  :  0.99999999999999989
0.99999999999999992  =  1.0  :  0.99999999999999989
0.99999999999999993  =  1.0  :  0.99999999999999989
0.99999999999999994  =  1.0  :  0.99999999999999989
0.99999999999999995  =  1.0  :  1.0
0.99999999999999996  =  1.0  :  1.0
0.99999999999999997  =  1.0  :  1.0
0.99999999999999998  =  1.0  :  1.0

更にもう少し桁数を多くして見ていくと、

0.99999999999999994454010  =  1.0  :  1.0
0.99999999999999994454011  =  1.0  :  0.99999999999999989
0.99999999999999994454012  =  1.0  :  0.99999999999999989
0.99999999999999994454013  =  1.0  :  0.99999999999999989
0.99999999999999994454014  =  1.0  :  0.99999999999999989
0.99999999999999994454015  =  1.0  :  0.99999999999999989
0.99999999999999994454016  =  1.0  :  1.0
0.99999999999999994454017  =  1.0  :  1.0
0.99999999999999994454018  =  1.0  :  1.0
0.99999999999999994454019  =  1.0  :  1.0

あれ?

0.99999999999999994454010 < 0.99999999999999994454011  =  False  :  False
0.99999999999999994454015 < 0.99999999999999994454016  =  True  :  True

こんなこともあるのかぁ … (@_@;)

それは横に置いておいて、上記を見ると 1.0 に丸めらる境界の辺りが何となくわかる。 1 / 3 の結果が丸められた値に 3 に極めて近い数値を掛けてみると、

0.33333333333333331 * 2.99999999999999977  =  1.0  :  0.99999999999999978
0.33333333333333331 * 2.99999999999999978  =  1.0  :  1.0

どうやらこの辺りが境界のようだ。

結局、 float の方は、値が丸められたことによって、 1 / 3 * 3 の結果が 1 になったということ。

 

Decimal による計算過程

まずは 1 / 3 の値は、

Decimal(1) / Decimal(3)  =  0.3333333333333333333333333333  :  Decimal("0.3333333333333333333333333333")

28 桁でピタッと丸められている。これに 3 を掛けるで、

Decimal(1) / Decimal(3) * Decimal(3)  =  0.9999999999999999999999999999  :  Decimal("0.9999999999999999999999999999")

ちょうどその 3 倍の値が正確に返ってくる。

5.5 decimal -- 10進浮動小数点数の算術演算 によると、

ハードウェアによる 2 進浮動小数点表現と違い、decimal モジュールでは計算精度をユーザが指定できます(デフォルトでは 28 桁です)。

デフォルトの設定のまま 29 桁の 9 を並べると、丸められて 1 となる。

Decimal("0.99999999999999999999999999999") * 1  =  1.000000000000000000000000000  :  Decimal("1.000000000000000000000000000")

上記で試した float において 1 に丸められてしまった値も 1 になることはない。

Decimal("0.99999999999999994454016") * 1  =  0.99999999999999994454016  :  Decimal("0.99999999999999994454016")

 

できるだけ誤差を少なく

結局、もし Decimal を使う場合で、計算結果が循環小数になる可能性があるなら、なるべく誤差を少なくするためには、乗算を除算よりも先にしておく。

Decimal(1) * Decimal(3) / Decimal(3)   =  1  :  Decimal("1")

 

5. 正確に計算するには

2008-09-09 - ひとり勉強会 には、RealLib を使う方法が紹介されている。

また、192:有理数を使う には、

Python に有理数型はありませんが、サンプルソースコード Rat.py にはクラスの拡張方法の例として有理数の実装が収められています。

そして、PEP 239 -- Adding a Rational Type to Python には、

    Python has no numeric type with the semantics of an unboundedly
    precise rational number.  This proposal explains the semantics of
    such a type, and suggests builtin functions and literals to
    support such a type. 

早く組込み関数で使えるようになるといいなぁ~。

2008年9月26日金曜日

なんでもできるはなんにもできない

「自由よりはむしろ制約を。」

Python で変数の型 (クラス) を調べる – types モジュール


1. type, isinstance 関数で、変数の型を調べる

Python で、変数の中身が、どのクラスに所属するのか、または、関数なのか調べたい。

2.1 組み込み関数 によると、 (装飾は、引用者による)

type(object)
object の型を返します。返される値は型オブジェクトです。
isinstance(object, classinfo)
引数 object が引数 classinfo のインスタンスであるか、 (直接または間接的な) サブクラスのインスタンスの場合に真を返します。
例えば、「数値」の場合、次のようにして、数値に対して type 関数を適用する。
print type(100)    #=> <type 'int'>
type 関数の結果を用いて、isinstance 関数の引数に指定した。
print isinstance(100, int)    #=> True

追記 (2011.12.10) :  Python 2.6 では、型の検査として、type 関数は推奨されていない。

type(object)(原文)

object の型を返します。オブジェクトの型の検査には isinstance() 組み込み関数を使うことが推奨されます。

 

関数に対して、type 関数は使えない

次に、関数に対して、type 関数を適用してみる。

def hoge():
    pass

print type(hoge)    #=> <type 'function'>

print isinstance(hoge, function) # ここでエラー 

実行したら、以下のエラーが表示された。

exceptions.NameError: name 'function' is not defined

あれ? (+_+)

 

2. types モジュールに、型の名前が定義されている

先ほどの type 関数の説明の続きに、 次のようにある。

標準モジュール types は、組み込み名を持っていない全ての組み込み型の名前を定義しています。

(同上より、装飾は引用者による)

3.6 types -- 組み込み型の名前 を参照すると、型の一覧が書かれていた。

このモジュールは標準のPythonインタプリタで使われているオブジェクトの型について、名前を定義しています

(同上より)

これによると、「関数」は、

FunctionType
ユーザー定義の関数またはlambdaの型です。
LambdaType
FunctionTypeの別名です。

よって、次のように書ける。

import types
print type(hoge) is types.FunctionType
print isinstance(hoge, types.FunctionType)

ちなみに、isinstance 関数において str , int などを型として利用できる理由は、 次の通り。

Python 2.2以降では、int()str()のようなファクトリ関数は、型の名前となりましたので、typesを使用する必要はなくなりました。

(同上より)

 

参考サイト

2008年9月25日木曜日

Python のジェネレータ (4) - 無限リスト

Python のジェネレータ (3) - 数字にコンマを振る のつづき

1. 有限リストに対して有限の繰り返し

「有限のリストを走査する」ためのジェネレータは、次のように書く。関数 g は、引数としてリスト L を受け取るとする。

def g(L):
    for e in L:
        yield e

関数の中で yield が使われているので、ジェネレータが返される。これをジェネレータ関数と呼ぶ。

 

2. 無限リストに対して無限の繰り返し

関数 g の中では、与えられたリストを走査するために、for ループを利用した。 for ループの代わりに

while True

を用いて、延々と回る無限ループに置き換えると、「無限リストを対象としたイテレータを生成する」ジェネレータとなる。

例えば、

「 3 の倍数の無限リスト」

が欲しい場合、次のように書くことができる。

def m3():
    i = 0
    while True:
        i += 3
        yield i

Python では、無限リストを表現する特別な記述方法はない。その代わり、「3の倍数」を延々と計算するループを利用する。関数の中で return ではなく、yield を用いることにより、1回のループごとに呼び出し元に制御が戻る。これにより、ループが最後まで終了するのを待たずに済む。

上記のジェネレータを for 文に渡した場合、ループが止まらない。ジェネレータに対する next() の呼出し回数を指定することにより、無限ループに陥らないようにする必要がある。

例えば、「 3 の倍数の無限リスト」の先頭から 10 個要素が欲しい場合は、

g = m3()
for i in range(0,10):
    print g.next()

結果は、

3
6
9
12
15
18
21
24
27
30

 

無限リストを有限のリストと重ね合わせる

リスト内包表記を用いると、

g = m3()
print [g.next() for x in range(0,10)]

リスト内包表記で生成するのは有限のリストで、これを無限のリストと重ね合わせる。

結果は、上記と同じ要素がリストとして返ってくる。

[3, 6, 9, 12, 15, 18, 21, 24, 27, 30]

zip 関数を使うなら、

print [b for (a,b) in zip(range(0,10), m3())]

 

3. ジェネレータをクラスから類推する

ジェネレータは、動作のイメージがつかみにくい。(+_+) しかし、クロージャを理解したときと同じ要領で、関数をクラスに見立てると理解しやすい。

例えば、上記の関数 m3 の場合で考える。

  1. 関数名 m3 をクラス名と見立てる。
  2. 変数 i は、インスタンス変数に相当する。
  3. while ループは、next() によって呼出されるメソッド。呼出される度に、インスタンス変数を更新する

と見なすことができる。

img02-19-2010[1]

 

n の倍数に一般化

上記の関数 m3 を、

「n の倍数を返すジェネレータ」

に変更する。

def m(n):
    i = 0
    while True:
        i += n
        yield i

先ほどと同じく、ジェネレータ関数 m をクラスと見なすと、引数 n を読み取り専用のインスタンス変数と見ることがでできる。

img02-19-2010[2]

 

4. 指定した要素数を取得する関数

次に、無限リストを生成するジェネレータから、

指定した要素を取得する関数

を作ってみる。 Haskell であれば take 関数に相当する。

 

有限リストを対象にする場合

まずは、対象を有限リストで考えてみる。指定した要素の数だけリストから取得するには、

def takeL(n,L):
    result = []
    for i in range(0,n):
        result.append(L[i])
    return result

print takeL(3, [1,2,3,4,5])

リスト内包表記を使うなら、

def takeL2(n,L):
    return [L[x] for x in range(0,n)]

print takeL2(3, [1,2,3,4,5])

リスト内包表記は、必要とする分だけ要素を重ね合わせて取得するための手段として用いた。

 

ジェネレータを渡して無限リストを対象にする

有限リストの代わりに、「無限リストを生成するジェネレータ」を渡すなら、

def take(n,g):
    result = []
    for i in range(0,n):
        result.append(g.next())
    return result

print take(10, m(3))

ただし、関数 m は先に定義した「n の倍数を返すジェネレータ」を指す。

 

ジェネレータを渡し、ジェネレータが返される場合

次に、「無限リストを生成するジェネレータ」を渡したら、「有限リストを走査するためのジェネレータ」が生成される関数を定義してみる。

def gtake(n,g):
    for i in xrange(0,n):
        yield g.next()

print list(gtake(10, m(3)))
print [x for x in gtake(10, m(3))]

この関数の動作イメージは、次のように理解すれば良い。 例えば、

gtake(5, m(3))

の場合で考える。関数の呼び出しを見ると、無限リストから 5 つ要素を取得する形をとっている。しかし、実際には無限リストを生成するジェネレータから、要素を順に計算した結果を得ている。

比喩的に言えば、gtake のジェネレータにより生成されたイテレータが動くと、それに伴なって m のジェネレータが生成したイテレータも呼応するように動く。

080925-012

take 関数は、実行すると、要素がリストとして返された。もし、take するリストが巨大であれば、要素数に応じたメモリが必要となる。それに対して、gtake 関数は、ジェネレータが返されるので、走査するためのメモリがあればよい。

極端な例で言えば、

「3 の倍数を 1000万個」 take 関数によって取得し、その後、10 で割り切れる数を 1 つだけ必要である

とする。 take 関数の場合、

for e in take(10000000, m(3)):
    if e % 10 == 0:
        print e
        break

「1000 万個の 3 の倍数のリスト」が生成されてから、その要素に対して 10 で割りきれるか確認することになる。

これに対して gtake 関数では、

for e in gtake(10000000, m(3)):
    if e % 10 == 0:
        print e
        break

実際には、「1000 万個の 3 の倍数のリスト」は生成されず、「 3 ~ 30 までの 3 の倍数」だけを確認して終了する。上記のコードにおいて break がなければ 1000 万個の 3 の倍数の要素を走査することになる。要素の末尾に到達せずに処理が終了するほど、gtake 関数の動作は有利となる。もし、末尾まで走査したとしても、メモリの使用量は take 関数よりも少ない。

 

5. Haskell の場合

Python で書いたコードを、Haskell で置き換えてみる。

m3 n = n : (m3 $ n+3)
-- 3 の倍数の無限リスト
m3' = m3 3

m x n = n : (m x $ n+x)
-- n の倍数の無限リスト
m' n = m n n

main = putStrLn $ show $ take 10 $ m' 3

遅延評価により、必要なときに必要なだけ処理されるので、無限リストを自然に扱える。

Python のジェネレータ (5) 再帰とジェネレータと Composite につづく…

Python の zip と map の違い

Python で map 関数の第 2 引数を操作の対象ではなく手段として使う」 の余談で見た map 関数の特殊な使い方。第 1 引数を None とした場合の zip 関数との違いを確認しておく。

例えば、

print zip([1,2,3], [1,2,3,4,5])
print map(None, [1,2,3], [1,2,3,4,5])

結果は、

[(1, 1), (2, 2), (3, 3)]
[(1, 1), (2, 2), (3, 3), (None, 4), (None, 5)]

つまり、

  • 短い方に合わせる zip
  • 長い方に合わせる map

 

zip とは

ところで、何で zip という名前なんだろう (?_?)

Yahoo!辞書 – zip によると、

1 …をチャック[ジッパー]で締める[開ける

二つのリストをチャックで締め合わせるということか?

 

zip を元に戻す

zip したものを元に戻すにはどうしたらいいのだろう? Haskell には unzip 関数があったけれど、Python には見あたらない。 (+_+) 自分でやるしかないかぁ。

例えば、

z = zip([1,2,3], [1,2,3,4,5])
print [a for a,b in z],[b for a,b in z]

それとも、

a,b = [], []
for x,y in z:
    a.append(x); b.append(y)
print a,b

ということは、これでもいけるか~

a,b = [], []
[(a.append(x),b.append(y)) for x,y in z]
print a,b

 

for とリスト内包表記の対応をちゃんとイメージで覚えておこっと。

080925-011

四角い七輪

七輪というと丸いイメージがあったけれど、

四角いのもあったのか。 (@_@;)

これ焼肉するときに使ってみたいなぁ~ ^^

 

(via NHK , 「生中継 ふるさと一番!」, 「太古の土で 切り出し七輪~石川県珠洲市~」 )


大きな地図で見る

 

切り出し七輪って何? によると、

切り出し七輪は、珪藻土を塊のまま切り出したものを削って
そのまま焼き上げるため、珪藻土の組織が破壊されず、
空孔が多く軽量で、形状によりますが一般的に強度があり
(ひび割れしにくく)、断熱性に優れています。

珪藻土 – Wikipedia によると、

珪藻が湖沼などで大量に増殖し死滅すると、その死骸は水底に沈殿する。死骸の中の有機物の部分は徐々に分解されていき、最終的には二酸化ケイ素を主成分とする殻のみが残る。

藻類 – Wikipedia とは、

淡水海水といった水圏に棲むものが最も多い。他に土壌性のものや、緑藻類のスミレモTrentepohlia aurea)のように気生藻として生活するものもある。

そんなものが原料だったとは…。 (@_@;)

Google スプレッドシートで連続データを作成する 二つの方法

1. 連続データを作成したい

Google スプレッドシートで、連続データを作成したい。

Excel では、フィル操作で連続データを作成する。

 

2. 式により、連続データを作成する

式を入力することによって、連続データを作成することができる。

080925-002

例えば、A 列が日付データで、A2 に今日の日付を「9/25」と入力する。

080925-003

A3 に、 A2 より 1 だけ大きい日付が入力されるように = A2+1 と入力。

080925-004

  上記により、A3 に 2008/09/26 と表示されるので、右隅の四角いハンドルをつかんで下方向へドラッグ。

080925-006

少し待つと、連続データが表示される。

 080925-009

  セルをダブルクリックして中身を見ると、「式」が入力されていることがわかる。つまり、 A2 に入力した内容を変更すれば連続データの内容も変更される。

 

3. 値により、連続データを作成する

値を入力して、連続データを作成する方法は知らなかった。 (@_@;)

 080925-010

A2, A3, にそれぞれ 9/25, 9/26 を入力する。

A2, A3 の 2 つを選択状態にして、右隅の四角いハンドルを下方向へドラッグする。

 080925-008

入力された連続データの中身を見ると、値として入力されていることがわかる。つまり、こちらは A2 に入力した内容を変更しても他の連続データは変更されない。

 

4. 飛び地のある連続データ

追記(2012/10/20): 飛び地のある連続データを作成したい。

SnapCrab_NoName_2012-10-20_5-17-28_No-00

A1 に 1 を入力する

一行離れた A3 に 2 または =a1+1 を入力する。

A1 ~ A4 を範囲選択し、右下のハンドルをつかみ、下方向へ移動する。

SnapCrab_NoName_2012-10-20_5-17-10_No-00  

2008年9月24日水曜日

Python で map 関数の第 2 引数を操作の対象ではなく手段として使う

操作対象としてのリスト

map 関数は、2.1 組み込み関数 によると、

map(function, list, ...)

functionlist の全ての要素に適用し、返された値からなるリストを返します。

操作対象のリストがあり、それを操作する関数を定義するというイメージ。

 

例えば、リストの各要素を 2 倍したいなら、

print map(lambda x: x*2, [1,2,3,4,5])

map 関数の第 2 引数のリストに対して、第 1 引数の関数を適用する。

リスト内包表記を使うなら、(cf. Python のリスト内包表記)

print [x*2 for x in [1,2,3,4,5]]

対象のリストがあって、そこから一つずつ取り出して関数を適用する。

 

リストは「料理される側」であって、関数はそれに対する「包丁」というイメージが自分の頭に固定された。 (+_+)

080923-001

 

手段としてのリスト

そういう固定観念があったので、前回「Python のジェネレータ (3)」で参考にした「数字にコンマを振る」の記事のコメントにあった map 関数の使い方を見て、なるほどと思った。なぜかと言うと、 map の第 2 引数が「関数全体の目的」に対して「手段」として使われていたから。具体的には、他のリストを参照するための手段としてのリスト。

080923-002

例えば、

a = "hogepiyofuga"
print map(lambda x: a[x], [1,4,7,8])

結果は、

['o', 'p', 'o', 'f']

map 関数の第 2 引数が、他の変数を参照するための手段として使われている。 map 関数の外にある変数を、無名関数 lambda の中から参照。

リスト内包表記で書くなら、

print [a[x] for x in [1,4,7,8]]

頭が固いので、こういうイメージ、 map に対して全然わかなかった。 (+_+)

 

普通に書くとすると、対象を中心に考え、

print [e for i,e in enumerate("hogepiyofuga") if i in [1,4,7,8]]

map と filter 関数を使うならば、

print map(lambda (i,e): e,
          filter(lambda (i,e): i in [1,4,7,8],
                 enumerate("hogepiyofuga")))

 

Ruby では?

Ruby で書くなら、

str = "hogepiyofuga".split(//s)
p [1,4,7,8].map{|e| str[e]}

あ~、そうだ。これ書いていて、以前から違和感を感じていたコードの理由がなんとなくわかった。脱線してしまうが、 Ruby では 繰り返しの for が内部で呼出しているというイテレータ。例えば、ブロックを 5 回実行するには、

(0...5).each{|i| p i}

for 文の書き方に比べて感覚的にしっくり来なかった。 ( 10.times do … という書き方はしっくり来るんだけど ^^; )

繰り返しの処理を行うとき、意識の中ではブロックの中に記述されているコードが処理・関心の中心にある。繰り返す回数は、あくまでも回数をこなすためにカウントする手段に過ぎない。問題に対して脇役というイメージ。だから、その手段となっている Range オブジェクト が最初に来て、主語のように居座り、それに対して処理をお願いするという形がピンと来なかった。上記の map の使い方もイメージとしては同様に感じていたために、何か微妙な感じがしたのかな。

ついでなので、対象の方を「主」にして書くと、

result = ""
"hogepiyofuga".split(//s).each_with_index do |e,i|
  result << e if [1,4,7,8].any?{|elem| elem == i}
end
p result

あれ?思ったよりも長くなった… (@_@;) 書き方違ってるのかな?

 

lambda

ちなみに、Python の 5.11 ラムダ (lambda) によると、

ラムダ形式で作成された関数は、実行文 (statement) を含むことができないので注意してください。

代入は、「代入文 (assignment statement)」、つまり statement の一種なので (cf. 6. 単純文 (simple statement))、 lambda の中に含むことはできない。 Ruby の proc{}, lambda{} とは異なる。(cf. Ruby のブロックと Proc)

だから、次のコードを実行しようとすると、「exceptions.SyntaxError: lambda cannot contain assignment」 というエラーが表示されてしまう。

a = list("hogepiyofuga")
map(lambda x: a[x] = "X", [1,4,7,8])

まぁ、しかし上記のような目的にそんな風には書かないか ^^; 普通は、

result = ""
for i,e in enumerate("hogepiyofuga"):
    result += "X" if i in [1,4,7,8] else e
print result

 

ジェネレータを使うなら、

def g():
    for i,e in enumerate("hogepiyofuga"):
        yield "X" if i in [1,4,7,8] else e

print "".join(x for x in g())

上記のジェネレータを一般化すると、

def g2(str, L, x):
    for i,e in enumerate(str):
        yield x if i in L else e

print "".join(x for x in g2("hogepiyofuga", [1,4,7,8], "X"))

 

どうしても、 lambda の中で外の変数を変更したいなら、オブジェクトのメソッド呼出しにすればいいか~。

class StringWrapper:
    def __init__(self, str):
        self.str = str
    def __getitem__(self, key):
        return self.str[key]
    def __setitem__(self, key, value):
        self.str = self.str[:key] + value + self.str[key+len(value):]
    def set(self, key, value):
        self.__setitem__(key, value)
    def __str__(self):
        return self.str

s = StringWrapper("hogepiyofuga")

# 参照
print map(lambda x: s[x], [1,4,7,8])

# 変更 
map(lambda x: s.set(x,"X"), [1,4,7,8]) 
print s

(cf. Python で要素を添字で参照する - 特殊メソッドを使って)

しかし、もうこれは lambda を使う意味がなくなってる  … ^^; これなら StringWrapper に、先ほどのジェネレータ g2 に相当するメソッドを作って、オブジェクトに対して普通にメソッド呼出しをするよね…。

 

余談

map 関数のドキュメントを読みなおしてはじめて気がついたけれど、これって複数のリストに対して適用できるのかぁ~ (@_@)

map(function, list, ...)

… 追加の list 引数を与えた場合、 function はそれらを引数として取らなければならず、関数はそのリストの全ての要素について個別に適用されます; 他のリストより短いリストがある場合、要素 None で延長されます。

(2.1 組み込み関数 の map より)

例えば、

print map(lambda x,y: x+y, [1,2,3],[10,20,30])
print map(lambda x,y,z: x+y+z,
          [1,2,3],[10,20,30],[100,200,300])
print map(lambda *x: [a*2 for a in x],
          [1,2,3],[10,20,30],[100,200,300])

結果は、

[11, 22, 33]
[111, 222, 333]
[[2, 20, 200], [4, 40, 400], [6, 60, 600]]

functionNone の場合、恒等関数であると仮定されます (同上より)

恒等関数 – Wikipedia とは、

変数を全く変えずにそのままの値で返す関数のこと。

zip 関数の説明には次のようにある。

zip() は初期値引数が Nonemap() と似ています。

例えば、

print map(None, [1,2,3],[10,20,30])

結果は、

[(1, 10), (2, 20), (3, 30)]

追記(2008.9.25) : 違いについては、Python の zip と map の違い参照。

セキュリティソフトを Avira AntiVir と Comodo Firewall の組み合わせへ移行 - Avast から乗り換えた

1. セキュリティ対策のアプリケーションを Avast から変更

SnapCrab_No-0865現在アンチウイルスとして Avast を使っている。もう何年もこのソフトにべったり。久しぶりにアンチウイルスソフトを変えてみようかな。

ところで、今流行っているアンチウィルスソフトは何だろう?できれば軽い動作のものに変更したい。

ここ一月以内で話題になっているものを検索。

ODN@OKWaveコミュニティー オススメのウィルス対策ソフト(無料)を教えて下さい」によると、

Avira AntiVir+Comodo Firewall フリーの組み合わせでは最強と思われます。

<アンチウイルス> Avira AntiVirが最もウイルス検出率が高く、スキャン速度や動作の軽さにおいても優れており一番オススメです。(…)
<ファイアーウォール> Comodo Firewall ProはHIPSがついているのでアンチウイルスが反応しない不審なプログラムでも動きを捉えることができるなど最高レベルの防御性能を誇る。

なるほど。 (@_@) この組み合わせを、一度試してみよう。

 

2. Avira AntiVir – アンチウィルス ソフト

SnapCrab_No-0866 Avira AntiVir - Wikipedia によると、

本ソフトの特徴として、高い検出力が挙げられる。VB100% Award[1]を11回中10回(…)受賞したほか、スウェーデンの第三者機関であるAV-Comparatives[2]が実施しているテストでは、常に上位に位置している。
… 未知ウイルス対策に重点を置いたテストでは、最高位であるAdvanced+評価を得ており、 …

追記(2014/10/19): 現在、Avira より検出力高く、動作が軽いアンチウィルスソフト Bitdefender Antivirus Free へ乗り換えた。

 

インストール

以下のいずれかのサイトより、Avira Free antivirus をダウンロードする。

最初に、これまでに使っていた Avast をアンインストールして再起動。

Avira AntiVir をウィザードに従いインストール。

インストールの途中、ニュースレターはいらないので登録しなかった。(以下の画面でチェックをはずした。)

080924-001
他はデフォルトの設定でインストールした。

インストール後に起動すると、アップデートが促されるのでアップデート。ここで宣伝が表示される。これが AntiVir Tips & FAQs - AntiVirとは? に書いてあった、

アップデートの際に広告が表示される。

タスクトレイには Avira AntiVir のアイコン 080924-012 が表示される。

 

Windows Defender を無効にする

追記 (2011.11.23) :

Knowledgebase for Home

Windows XP または Windows Vista に AntiVir モジュールをインストールした後に、いずれかのモジュールが非アクティブの状態になる場合、Windows Defender を非アクティブにすることをお勧めします。

Windows Vista/7でWindows Defenderを無効化する - @IT

市販されているほとんどのサードパーティ製ウイルス対策ソフトウェアはスパイウェアも検出・駆除可能なため、Windows Defenderと同時に稼働させると二重にスパイウェアを監視することになる。このような状態だと、メモリ容量やプロセッサ・パワーなどのリソースを無駄に消費することになるため、Windows Defenderは停止した方がよい。

上記を参照して、Windows Defender を停止した。

 

3. Comodo – ファイアウォール

更新(2015/2/3): ⇒ ファイアウォールのど定番 Comodo Firewall

Firefox 3.0.2 で文字の選択がしやすくなった

 Firefox 3 - もじら組Wiki によると、「Fx 3.0.2で修正予定のバグ」の一つとして、

日本語の単語をダブルクリックしたときの選択範囲がFx2とFx3以降で異なる

具体的には、

例文:
カレーは大好きです。甘口より辛口が好きです。

 この文章の「カレー」をFx2でダブルクリックすると「カレー」が選択されますが、Fx3
以降では「カレーは大好きです」と言うように句読点の手前、全文が選択されてしまいま
す。

(bug 6224 – 日本語の単語をダブルクリックしたときの選択範囲がFx2とFx3以降で異なる より)

なるほど、これで文章の選択ががしやすくなった。 ^^

 

Nightly Tester

Tab Mix Plus が動かなくなってしまったので、Nightly Tester Tools :: Firefox Add-ons をインストール。

メニューの「ツール > アドオン」 よりアドオンの一覧を表示させ、Tab Mix Plus の上で右クリック > 「Override Compatibility」 により有効化。

2008年9月23日火曜日

Firefox で検索バーを広くする

Firefox を使っていて、もったいないと思う場所は一つ。メニューの右側。

表示 > ツールバー > カスタマイズ」において、ロケーションバー (アドレスを入力するところ) をメニューの右側へ移動。

080923-006

 

これで、URL の表示も検索する単語もバッチリ見えて快適。 ^^

※ 「ツールバーのカスタマイズ」において、ダイアログ下の「小さいアイコンを使用」にチェックを入れておくと、すっきりしてなおいいかも。

solipo で polipo

以前に Polipo を試したことがあるが、Firefox 3 になってから使わなくなっていた。久しぶりに入れてみることに。

solipo を使うとセットアップが楽。ダウンロード & 更新履歴 よりダウンロードしてインストール。起動するとタスクトレイに常駐。

 

Firefox の設定

「オプション > 詳細 > ネットワーク > 接続設定」 でインターネット接続の設定ウィンドウが表示される。「手動でプロキシを設定する」にチェックを入れ、HTTP プロキシのフィールドには localhost , ポートに 8123 と入力。「すべてのプロトコルでこのプロキシを使用する」にチェックを入れた。

訂正(2008.10.1) : 「すべてのプロトコルでこのプロキシを使用する」のチェックははずした。

080923-002

 

結果

やはり、少し早くなるかなぁ~。

2008年9月22日月曜日

Python のジェネレータ (3) - 数字にコンマを振る

Python のジェネレータ (2) のつづき

1. ジェネレータを使う方法

前回と同じく、ジェネレータを使う例を考える。

Python のチュートリアル 5.5.7 レシピ に、moneyfmt() という関数が書かれている。とりあえず、これは横に置いておく。

「数字にコンマを振る」ために、ジェネレータを使うことをイメージする。

  1. 数をリストに見たて、末尾から 3 つずつ要素を返すジェネレータを作成。
  2. そのジェネレータからリストを生成し、
  3. 要素を逆順にして、
  4. 最後にコンマで要素をくっつける。

080922-002

実装。

def gCommaStr(num):
    result = ""
    for i,e in enumerate(reversed(str(num))):
        if not i == 0 and i % 3 == 0:
            yield result
            result = ""
        result = e + result
    yield result

print ",".join(list(reversed([x for x in gCommaStr(1234567890)])))

一応、コンマを打つ位置を可変にしておくと、

def gCommaStr2(num,n):
    result = ""
    for i,e in enumerate(reversed(str(num))):
        if not i == 0 and i % n == 0:
            yield result
            result = ""
        result = e + result
    yield result

print ",".join(list(reversed([x for x in gCommaStr2(1234567890,3)])))

 

2. 再帰を使った方法

リスト操作なので、ついでに再帰の練習もしておこう。

数が小さいときは、

1 --> 1
12 --> 12
123 --> 123

そのまま返す。

コンマがつけられる最小の数値のリストは、

1234 --> 1,234

これが再帰をするときの基本的なコンポーネントになりそうだ。

続きを考えると、

12345 --> 12,345
123456 --> 123,456
1234567 --> 1,234,567
12345678 --> 12,345,678

ぼ~っとながめていると、何となく繰返しになっている構造が見えてくる。 (@_@;) 上記の最後のパターンを見ていたら、 「2,345」 と 「5,678」 というように分割して考えれば、先ほど考えた基本的なコンポーネントが結合した形と見ることができることに気がついた。  「5」  が重複しているが、これは再帰呼出しの後に削除すればいいような感じ。 「12345」 なら、 「12」 と 「2,345」 。 「123456」 なら、 「123」 と 「3,456」 というような部分に分割できると考える。

080922-006

これで数値が大きくなっても、基本的なコンポーネントとそれ以外の部分に分けて考え、関数を再帰的に適用すればいいような気がしてきた。

080922-005

さて、実装~ (+_+)

def recCommaStr(L):
    if len(L) <= 3:
        return L
    elif len(L) == 4:
        L.insert(1, ","); return L
    else:
        return recCommaStr(L[:-3])[:-1] + recCommaStr(L[-4:])

num = 1234567890
print "recCommaStr(L): " + "".join(recCommaStr(list(str(num))))

引数 L は、数値を文字列にした後、リストにしたものを渡すようにした。

何も知らずにこのコードを見たら、読む気が失せるけれど ^^; 、わかっていれば再帰的な実装はやり方を素直に実装しているように感じる。

これも同じくコンマが打たれる位置を指定できるように拡張すると、

def recCommaStr2(L,i):
    if len(L) <= i:
        return L
    elif len(L) == i+1:
        L.insert(1, ","); return L
    else:
        return recCommaStr2(L[:-i],i)[:-1] + recCommaStr2(L[-(i+1):],i)

num = 1234567890
print "recCommaStr2(L,i): " + "".join(recCommaStr2(list(str(num)),3))

引数 i がコンマの位置を現わす。

 

3. 割り算で分割する方法

あ~、そうだ。数値を 1000 で割っていけば、コンマの打ちたい分の数値だけ取得できるかぁ。

def divCommaStr(num):
    result = str(num) if num < 1000 else ""
    while num > 1000:
        result = "," + str(num % 1000) + result
        num /= 1000
        if num < 1000:
            result = str(num) + result
    return result

print "divCommaStr(1234567890): " + divCommaStr(1234567890)

あれ?何かややこしい。 (+_+) こういうの考えるの苦手。脳みそ混乱してきた。 パタッ(o_ _)o~†

 

コンマの位置を可変にすると、

def divCommaStr2(num, i):
    assert i > 0
    divNum = 10 ** i
    if num < divNum: return str(num)

    result = ""
    while num > divNum:
        result = "," + str(num % divNum) + result
        num /= divNum
        if num < divNum:
            result = str(num) + result
    return result


print "divCommaStr2(1234567890, 3): " + divCommaStr2(1234567890, 3)

 

4. クラスを使った方法

クラスを使って実装してみる。コンマ付きの数字を表わす CommaStr クラスを作成。コンマを付けるのは、文字列としてオブジェクトの情報を出力するときに、末尾から数値を 3 つ置きに走査する時点でつけることにした。

class CommaStr:
    def __init__(self, num, i):
        self.i = i
        self.nums = str(num)
            
    def __str__(self):
        result = ""
        for i,e in enumerate(reversed(self.nums)):
            if not i == 0 and i % self.i == 0:
                result = e + "," + result
            else:
                result = e + result
        return result

print "CommaStr(1234567890, 3):", CommaStr(1234567890, 3)

これだとクラスを使う意味があまりないなぁ。 ^^; グローバル変数と関数を組み合わせる方法と大差なし。でも、クラスで考えるというのは何となくイメージしやすい。

 

自分の好みとしては、同様にコンマ付きの数字全体を CommaStr クラスで表わし、各数字を Num クラスとする。 Num クラスには、インスタンス変数として数値とコンマをつけることができるフィールドを用意しておく。

080922-008

実装。

class CommaStr2:
    def __init__(self, num, i):
        self.i = i
        self._createNums(num)

    def _createNums(self, num):
        self.nums = []
        for e in str(num):
            self.nums.append(Num(e))
        self.setComma(",")
            
    def setComma(self, comma):
        for i,e in enumerate(reversed(self.nums)):
            if not i == 0 and i % self.i == 0:
                e.comma = comma

    def __str__(self):
        return "".join(str(x) for x in self.nums)

class Num:
    def __init__(self, num, comma=""):
        self.num = num
        self.comma = comma
    
    def __str__(self):
        return str(self.num) + self.comma

print "CommaStr2(1234567890, 3):", CommaStr2(1234567890, 3)

全体は長くて効率も悪いけれど、全体の構造の見通しがよく、各メソッドはシンプルでええわぁ~。 ^^

Python のジェネレータ (4) - 無限リスト につづく…

Python でアバウトな時間計測

1. 日付と時間を持つ datetime オブジェクトで時間の計測

特定の処理にかかる時間を計測したい。

6.10.4 datetime オブジェクト によると、

datetime オブジェクトは date オブジェクトおよび time オブジェクトの全ての情報が入っている単一のオブジェクトです。(…)

now([tz])

現在のローカルな日付および時刻を返します。オプションの引数 tzNone であるか指定されていない場合、このメソッドは today() と同様ですが、可能ならば time.time() タイムスタンプを通じて得ることができるより高い精度で時刻を提供します (例えば、プラットフォームが C 関数 gettimeofday() をサポートする場合には可能なことがあります)。

試してみる。

from datetime import datetime
from time import sleep

start = datetime.now()
print start

sleep(1)

end = datetime.now()
print end
print end -start

結果は、

2008-09-22 09:53:44.375000
2008-09-22 09:53:45.375000
0:00:01

 

2. time 関数で時間を計測

6.11 time -- 時刻データへのアクセスと変換 によると、

ほとんどの関数が利用可能ですが、全ての関数が全てのプラットフォームで利用可能なわけではありません。このモジュールで定義されているほとんどの関数は、プラットフォーム上の同名の C ライブラリ関数を呼び出します。(…)

time()

時刻を浮動小数点数で返します。単位は UTC におけるエポックからの秒数です。時刻は常に浮動小数点で返されますが、全てのシステムが 1 秒より高い精度で時刻を提供するとは限らないので注意してください。

試してみる。

from time import time
start = time()
print start

sleep(1)

end = time()
print end
print end - start

結果は、

1222044825.38
1222044826.38
1.0

 

3. その他

追記(2008.9.22) : より正確な時間を計測するには、timeit モジュールを利用する。

18.2. Using the timeit Module には

「The most important thing you need to know about optimizing Python code is that you shouldn't write your own timing function.」

とあることに注意。 (via 実行時間計測の基本)

Python でメソッドの実行時間の計測 につづく…

2008年9月21日日曜日

Python で for ループを回し切った後に行う処理と、深部からの脱出

1. for ループを回し切った後に処理を行う

Python では、for ループを回し切った後に行う処理を指定できる。

7.3 for 文 によると、

最初のスイート内で break 文が実行されると、else 節のスイートを実行することなくループを終了します。

for に対応した else を書けば良い。試してみる。

for i in range(0,5):
    if i == 3: break
    print i
else:
    print u"回し切った!"


for i in range(0,5):
    print i
else:
    print u"回し切った!"

結果、

0
1
2
0
1
2
3
4
回し切った!

for ループの途中で break されなければ else 節が実行される。

 

2. for ループからの脱出

Python には、ネストされたループの中で一気に脱出する Java の goto みたいなのはないのかな?

には、ループを脱出方法として、例外が使われている。

class END(Exception):
    pass

try:
    for j in range(0,3):
        for i in range(0,5):
            if i == 4 and j == 2:
                raise END
            print i, j
except END:
    print "END"

には、モジュールを使う方法が書かれている。

Greasemonkey で特定のリンクをハイライト

1. 特定のリンクを目立たせたい

条件を指定して、一致したリンクをハイライトして目立たせたい。ただし、

  1. 条件は、正規表現で指定。
  2. 除外条件も設定できること。

デフォルトでは、開いているページのホスト名ではじまるリンクのみをハイライトする。

 

2. 使い方

前提: Greasemonkey がインストールしてあること。

こちらをクリックしてインストール → highlight_link.user.js

 

3. 実装方法

リンクを目立たせるために、DOM 要素の style プロパティを設定する。

CSS – MDC には、Gecko1.8+ DOM において、要素の style プロパティで設定できる項目の一覧がある。

The following is a list of the CSS properties that are supported in the Gecko 1.8+ DOM and accessible by means of the style property on elements.

style プロパティを設定するとき、同じ要素に対して複数のプロパティを設定する。この場合、JavaScript の with – MDC を使うと書きやすい。

要素の反復については、「JavaScript における 3 つの for 文」を参照。

 

4. 実装

使い方は、

  • `INCLUDE_REGEXP’ に、 ハイライトしたい A タグの src に含まれる文字列を正規表現で指定する。
  • `EXCLUDE_REGEXP’ に、 ハイライトしたくない A タグの src に含まれる文字列を正規表現で指定する。 (省略可)

// ==UserScript==
// @name           highlight link
// @namespace      http://jutememo.blogspot.com/
// @description    特定のリンクを目立たせる
// @include        http://*
// ==/UserScript==

(function() {
 // 対象にしたい a タグの src に含まれる文字列を正規表現で指定
 var INCLUDE_REGEXP = "^" + location.protocol + "//" + 
          location.hostname + ".*()";
 // 除外したい a タグの src に含まれる文字列を正規表現で指定
 var EXCLUDE_REGEXP = "";
 
 // 指定された要素を目立つようにする
 // cf. CSS - MDC 
 function highlightElem(e){
  with(e.style){
   backgroundColor = "#fcc"
   borderWidth = "1px";
   borderColor = "#f00";
   borderStyle = "solid";
   borderWidth = "3px";
//   fontSize = "large";
  }
 }
 // リンクを除外するための正規表現オブジェクトを生成
 function createExcludeRegExp(){
  return EXCLUDE_REGEXP ? new RegExp(EXCLUDE_REGEXP) : null;
 }
 function highlight(re, notRe){
  var anchors = document.getElementsByTagName("a");
  for (var i = 0; i < anchors.length; i++){
   href = anchors[i].href;
   if (!href.match(re) || href.match(notRe)) continue;
   highlightElem(anchors[i]);
  } 
 }
 
 highlight(new RegExp(INCLUDE_REGEXP), createExcludeRegExp())
})();

2008年9月20日土曜日

Logicool Qcam のユーティリティをアップデート

追記(2008.9.22) : やっぱりダメだった。またスカイプ暴走。 (+_+) コントロールパネル > プログラムの追加と削除 より、 Logicool Qcam のみ削除した。


以前、 Logicool Qcam に付属していたユーティリティを常駐させていると、Skype の調子が悪くなった。そのため、ドライバのみをインストールして、タスクトレイに常駐するユーティリティを削除した。( Web カメラを使うと Skype が頻繁に暴走する ) 最近、そのユーティリティがアップデートしたようなので、再度インストールして調子を見てみた。

 

再インストール

Logicool > サポート&ダウンロード > ダウンロード からユーティリティをダウンロード。インストール後に、タスクトレイのアイコンを右クリック > 「アップデートをチェック」を選択すると、新しいアップデータがある旨が表示されるのでダウンロードしてインストール。再起動。

デバイスマネージャでドライバの日付を見ると、ドライバ自体は前回と変わらないようだ。多分。新しくなったのは、タスクトレイに表示されている目玉のおやじのようなアイコンのユーティリティ。

080920-003

 

Skype も以前と比べるとバージョンアップがされている。 (前回の記事は 2008.8.7) だから、どちらが原因だったのか、それもとも交互作用だったのかわからない。そういえば、 Windows Update もあったかもしれないから、もう何が何だか… (@_@;) 現在使っている Skype のバージョンは、、

バージョン 3.8.0.154 ファイルサイズ 21 MB。公式リリース。リリース日: August 14, 2008。ファイル名: SkypeSetup.exe

 

結果

使って数時間が経過するが、今のところ以前のようにユーティリティが頻繁に起動し、それと同時にカメラのランプがパカパカと点滅するということがない。また、Skype が暴走するということもない。 ^^

 

余談

Skype for Windowsベータ版 のスクリーンショットを見たら、使い勝手が向上しているかもという期待が。

Google Sites をウェブマスターツールに登録

友人も Google Sites を使ってサイトを作ってみた。しかし、一ヶ月以上経っても Google のインデックスに登録される気配がない。おかしいなぁ (@_@;) Google に認識されるようにウェブマスターツールを使ってみようか。

 

サイトの登録

ウェブマスターツールを使う前に、まずは基本の Google に URL を追加

 

ウェブマスター ツール

Google Sites で作成されたサイトの確認 - ウェブマスター向けヘルプ センター の説明に従った。

ウェブマスター ツール において、Google Sites で作成したサイトの URL を入力し、「サイトを追加」ボタンを押す。

「サイトを確認」において、セレクトボックスより「メタ タグを追加」を選択し、表示されたメタタグをコピー。

080920-002

 

 Google Sites において、 Site settings > Other Stuff の「Google Webmaster Tools verification」に上記でコピーしたメタタグを入力し、設定を保存する。

080920-003

 

ウェブマスター ツールにおいて、「確認」ボタンを押して、Google Sites を確認する。

JavaScript における 3 つの for 文

1. for – 配列の要素を添字で順に参照

配列の要素を添字で順に参照する for 文。

例えば、「HTML の全 A タグを取得して、その内容を出力」する場合、以下のように書く。

anchors = document.getElementsByTagName('a');
for (var i=0; i<anchors.length; i++){
 console.log(anchors[i])
}

試しに `for each...in - JavaScript | MDN’ のページで、上記の文を Firebug のコンソールで実行する。結果は以下の通り。

<a href="#pageContent">
<a href="#searchInput">
<a title="メインページへ" href="/">
<a id="toolslink" href="#sitetools">
(略)
<a href="/index.php?title=Special:Article&type=backlinks&pageid=29784">
<a class="dismiss" onclick="return MTMessage.Hide();" href="#">
<a id="MTMessageDetailsLink" class="details" onclick="return MTMessage.ShowDetails(this);" href="#">

ページ中に含まれる 「A タグ」が全て出力されたことが分かる。

 

2. for each - 配列の要素の「値」を順に走査

添字を利用する for文を使わずに、配列の要素を順に走査したい for each 文を利用する。

for each...in – MDC によると、

指定された変数を、オブジェクトの全プロパティの値に対して反復します。異なるそれぞれのプロパティに対し、指定された文が実行されます。

この文は JavaScript 1.6 で作られた。

New in JavaScript 1.6 – MDC によると、

JavaScript 1.6 は Firefox 1.5 以降でサポートされています。

先ほどの例と同じページを対象にして、for each … in を試してみる。

anchors = document.getElementsByTagName('a');
for each(var p in anchors){
 console.log(p);
}

結果は以下の通り。

<a href="#pageContent">
<a href="#searchInput">
<a title="メインページへ" href="/">
<a id="toolslink" class="" href="#sitetools">
(略)
<a href="/index.php?title=Special:Article&type=backlinks&pageid=29784">
<a class="dismiss" onclick="return MTMessage.Hide();" href="#">
<a id="MTMessageDetailsLink" class="details" onclick="return MTMessage.ShowDetails(this);" href="#">
70
item()
namedItem()

あれ?さっきよりも、表示されるものが増えてしまった。 (@_@;)

 

HTMLCollection

例のコード内でアンカータグを取得するメソッドは、document.getElementsByTagName()。これによって返されるものは、HTMLCollection

Document Object Model HTML によると、

An HTMLCollection is a list of nodes. An individual node may be accessed by either ordinal index or the node's name or id attributes.

HTMLCollection は node のリスト。個々の node にはインデックスで参照できる。

HTMLCollection の定義を確認する。、(同上より)

interface HTMLCollection {
  readonly attribute unsigned long    length;
  Node               item(in unsigned long index);
  Node               namedItem(in DOMString name);
};

先ほどの出力の最後に表示さたもが、上記のプロパティに対応している。

出力内容は for each の説明にある通り、 HTMLCollection の全プロパティが表示されたということ。

 

3. for … in - 配列の要素の「値」を得る

for each  に似ていて、同じく全プロパティに対して走査するのが for … in 。

for...in – MDC によると、 for 文と同じく昔からあるようだ。同じく試してみると、

anchors = document.getElementsByTagName('a');
for (var p in anchors){
 console.log(p);
}

結果は、

0
1
2
3
(略)
67
68
69
length
item
namedItem

確かに for each に似ている。しかし、異なる点は、

for each...in - for...in に似ていますが、プロパティ名そのものではなく、オブジェクトのプロパティの値に対して反復します。 (同上より)

 

4. まとめ

追記 (2009.3.13) : JavaScript はハッシュが基本的な構造。 (cf. JavaScript のハッシュとオブジェクト)、

ハッシュの用語で言うなら、

  1. for ... in は「キー」を得られる。
  2. for each ... in は「値」を得られう。

と覚えておけばいい

var hash = {hoge:"ほげ", piyo:"ぴよ"}; 

// キーを走査
for (var key in hash){
    console.log(key);
} 

// 値を走査
for each (var val in hash){
    console.log(val);
}
結果は、
hoge
piyo
ほげ
ぴよ

2008年9月19日金曜日

Spket IDE で Greasemonkey

Greasemonkey の編集エディタを Spket IDE に設定

Spket IDE については、「Spket IDE, Firebug を使って JavaScript」を参照。

ウィンドウ右下の Greasemonkey のアイコンで右クリック > ユーザスクリプトの管理

080919-001

 

表示されたウィンドウの左下の「編集」ボタンを押す。

080919-003

 

使用するエディタを設定するためのダイアログが表示されるので、c:\Program Files\spket\spket.exe を選択する。この設定は、 ロケーションバーで、 about:config > 「greasemonkey.editor」に保存される。エディタを変更したい場合は、この値を変える。

 

スクリプトの作成

先ほどと同じ Greasemonkey のアイコンで右クリック > 新規ユーザースクリプト でダイアログが表示される。

080919-004

OK ボタンを押すと、 Spket IDE で開かれる。

 

テンプレート

作成するスクリプトは、他のスクリプトに影響を与えないようにブロックスコープ入れた方がいいみたい。 (cf. JavaScript のブロックスコープ)

// ==UserScript==
// @name           hoge
// @namespace      http://jutememo.blogspot.com/
// ==/UserScript==
(function () {
        //ここに実際のコードを書く
})();

 

ログの出力

3.2. Logging with GM_log [Dive Into Greasemonkey] によると、

Greasemonkey provides a logging function, GM_log, that allows you to write messages to JavaScript Console.

GM_log(“ログ情報”) というように使う。 (cf. GreaseMonkeyでalertを使わないでデバッグする方法 )

 

表示先

Firefox のメニューより、「ツール > エラーコンソール」を選択すると、デバッグ情報が表示されるウィンドウがでてくる。

080919-005

 

追記(2008.9.20) : Firebug のコンソールの右上のオプション「クロームのエラーを表示」にチェックを入れたら、Firebug でも GM_log() によるログの出力が表示された。

追記(2009.9.25) : 「クロームのメッセージを表示」のチェックも必要。

080920-007

また、Greasemonkey + Firebug + GM_log - 冬通りに消え行く制服ガールは、夢物語にリアルを求めない。 – subtech によると、

Firebug 1.0 以降では extensions.firebug.showChromeMessages を true にしないと GM_log のメッセージが表示されないらしい

Python のジェネレータ (2) - リストの走査

Python のジェネレータ (1) - 動作を試す のつづき

1. ジェネレータをどこで使うのか?

ジェネレータの動作について、何となく雰囲気を理解できた。しかし、どのような目的に使うのか、今一分からない

「あ!これはジェネレータを使うと、問題を解くのが楽だ。」

「ジェネレータを使った方がシンプルに書ける」

ということが分かるようになりたい。

 

イテレータを置き換えるジェネレータ

最初に覚えたことは、

ジェネレータを使うと、イテレータを簡単に作成できる。

ということ。イテレータとは、複数のオブジェクトに対して、要素を一つづつ辿るための手段。よって、ジェネレータは、複数の要素を辿るために利用できる。

080329-004例えば、Group クラスが Person クラスに対して責務があるとする。イテレータを自前で実装するには、境界条件を考えながら、イテレータプロトコルに沿うようにする。

自前で実装したイテレータは、ジェネレータで置き換えることができる。

イテレータを自前で実装することと比べると、ジェネレータを使った方がシンプルに書ける。

 

2. ジェネレータを使い、リストを走査する例

ジェネレータを使うことができる例を挙げる。

には、「リストの各々の要素に対して、右隣の要素との差をリストとして返す」処理の例が書かれている。

>>> a = [0,1,2,3,4,5] (略)

… 一つ前の要素との差を求めたかった。a[1]とa[0]の差は1といった感じで、a=[0,1,1,1,1,1]になって欲しい。0番目は何もしない。

ジェネレータを使わずに定義するなら、

def sa(ary):
    result = []
    for x in zip(ary[:-1], ary[1:]):
        result.append(x[1] - x[0])
    return result

def sa_wrapper(ary):
    return ary[:1] + sa(ary)
    
print sa_wrapper([0,1,2,3,4,5])

関数 sa をリスト内包表記で置き換えると、

def sa(ary):
    return [b-a for a, b in zip(ary[:-1], ary[1:])]

この問題に対して、ジェネレータを使ってみる。

080918-003最初に、ジェネレータを利用したときのイメージを考える。

  1. 対象のリストの要素に対して、0 番目と 1 番目の要素から走査するジェネレータを2つ想定する。
  2. 各々のジェネレータから要素を順に取り出し、関数 zip でジェネレータ g1 から取り出した値を g2 から引いた値をリストにする。
  3. 最後に、元の先頭の要素を求めるリストの先頭にくっつける。

ジェネレータを使わない場合、

ary[:-1], ary[1:]

のようにして部分リストを取得する。この処理をジェネレータで置き換える。

def gen(ary, i):
    u""" リストの i 番目の要素から走査するジェネレータ """
    for e in range(i, len(ary)):
        yield ary[e]

a = [0,1,2,3,4]
print [a[0]] + [a-b for a,b in zip(gen(a,1), gen(a,0))]

 

ジェネレータを使うメリット

ジェネレータは、イテレータのように要素を1つずつ取り出しては処理を行う。そのため、ジェネレータを使うメリットは、

ary[:-1], ary[1:]

のように部分リストを完全に取得する必要がないこと。もし、対象のリストがとても長く、

「要素を検査した結果、すぐに値を返す」

というような処理が含まれる場合、効率的に計算が行われる。

 

3. 再帰的な処理で置き換える

以下、ジェネレータとは関係がない。上記の例を再帰的な処理で置き換えてみる。

  1. 空のリストが来たとすると、そのまま返す。
  2. 要素が一つの場合も、何もせずにそのまま返す。
  3. 要素が二つのときは、2 番目の要素から先頭の要素を引いた値をリストにして返す。
  4. 要素が三つ以上のときは、同様に2 番目の要素から先頭の要素を引いた値をリストにしたものと、2 番目以降の要素を再帰的に呼出した結果のリストと結合して返す。
def rec(ary):
    if len(ary) <= 1:
        return ary
    elif len(ary) == 2:
        return [ary[1] - ary[0]]
    else:
        return [ary[1] - ary[0]] + rec(ary[1:])
    
def rec_wrapper(ary):
    if ary == [] or len(ary) == 1: return ary
    return [ary[0]] + rec(ary)

a = [0,1,2,3,4]
print rec_wrapper(a)

先頭の要素を、リストの先頭に持ってくる処理も再帰呼出しの中に含めるように変更する。

def rec2(ary):
    if len(ary) <= 1:
        return ary
    elif len(ary) == 2:
        return [ary[0]] + [ary[1] - ary[0]]
    else:
        return rec2(ary[:-1]) + [ary[-1] - ary[-2]]

print rec2(a)

 

要素を指定できるようにする

上記では、ジェネレータを使って書いたときの柔軟さが失われている。

ジェネレータを使った書き方は、引数 i によって隣接する要素だけではなくて、i 個離れた隣の要素から引いた値のリストを返すことができる。上記の再帰関数も、同じように指定された i だけ隣の要素から引いたリストを返すように変更してみよう。

080919-006まずは、具体的な例で考える。例えば、i = 3 で、3 つ隣の要素から各要素を引いた値のリストを得る場合。このとき、リストが返されるために、元になるリストは少なくとも 要素を 4 つ持っていないといけない。 要素が 3 つ以下であれば、何せずに元のリストをそのまま返すとする。逆に 5 つ以上であれば関数を再帰的に適用する。

これを一般的に書くならば、

  1. 引数 i + 1 と、リストの大きさが同じ場合、 i 番目の要素から先頭の要素を引いた値をリストにして返す。
  2. 引数 i  >= リストの大きさであれば、何せずにそのまま返す。
  3. それ以外のときは、再帰的に関数を適用
def rec3(ary,i):
    if len(ary) <= i:
        return ary
    elif len(ary) == i+1:
        return [ary[i] - ary[0]]
    else:
        return [ary[i] - ary[0]] + rec3(ary[1:],i)

def rec_wrapper3(ary,i):
    if len(ary) <= i: return ary
    return [ary[0]] + rec3(ary,i)

print rec_wrapper3(a,1)

同じように関数 rec2 も変更してみる。

def rec4(ary,i):
    if len(ary) <= i:
        return ary
    elif len(ary) == i+1:
        return [ary[0]] + [ary[i] - ary[0]]
    else:
        return rec4(ary[:-1],i) + [ary[-1] - ary[-1-i]]

print rec4(a,1)

シンプルに見えるけど、見直してもすぐに理解できなくなってしまった。 パタッ(o_ _)o~†

 

処理を分割する

上記の定義は複雑すぎる。理由は、処理が適切に分割されていないため。

行なっている処理は、2つに分かれている。

  1. リストの各々の要素に対して、右隣の要素との差をリストとして返す。
  2. 対象の先頭要素を、結果の先頭に追加する。

予め Haskell で書いてみる。

sa (x:[])   = []
sa (x:y:xs) = y-x : sa (y:xs)

sa_wrapper xs | length xs <= 1 = xs
              | otherwise      = head xs : sa xs

Python で書きなおすと、

def sa(ary):
    if len(ary) <= 1: return []
    else: return [ary[1]-ary[0]] + sa(ary[1:])

def sa_wrapper(ary):
    if len(ary) <= 1: return ary
    else: return ary[:1] + sa(ary)

これで読みやすくなった。

Python のジェネレータ (3) につづく…