2008年10月31日金曜日

Firefox で特定のサイトのクッキーを受け付けないようにする

メニューより、「ツール > オプション」

「プライバシー > Cookie」の「例外サイト」ボタンを押す。

Cookie フィルタダイアログが表示されるので、サイトのアドレスを入力し、Disable ボタンを押す。

081031-002

落ち着く音楽はないかな? - I Will See You Again

イライラしているときは、いくら昔好きだったレーベルだとは言え、さすがに Tresor を聞くことはあまりない。アコースティックギター で何か落ち着く感じのものはないかなと、Last.fm を BGM に流していたら、いい音楽に出会えた。 ^^

YouTube - I will see you again - Andy Mckee

アンディ・マッキー – Wikipedia によると、

アメリカのアコースティック・ギタリスト。現在CANdYRATレコードに籍を置く彼には、その珍しい演奏スタイルと作曲の才能のために、厚い支持がある。

2006年後半、自身の代表曲である"Drifting"の演奏を収めたクリップはYouTubeのトップ・ページ"Featured Video"に採り上げられ、2006年11月までに500万近くの閲覧数を集めた。…

へ~、知らなかった。

それにしても「影響を受けた演奏家」のところに、

パンテラ

パット・メセニー

なんというギャップ。

以下が上記に書かれている Drifting 。すごい演奏法だ。(@_@;)

これも良かった ^^

こちらは、「I Will See You Again」と同じく「DREAMCATCHER」に。

 

関連サイト

2008年10月30日木曜日

Haskell のリストのデータコンストラクタによるパターンマッチ

データコンストラクタによるパターンマッチ

データコンストラクタによるパターンマッチというと、例えば、以前に Person 型を定義して、Show クラスのインスタンスにしたときに実装した show メソッドの引数での利用が挙げられる。(cf. Haskell で代数的データ型の値を print で出力には)

    data Person = Person {name :: String,
                          age :: Int} 

    instance Show Person where
        show (Person name age) = "Person: " ++ name ++ ", " ++ show age

    main = print (Person "Tarou" 20)

 

リストにも当然ながらデータコンストラクタが存在する。(cf. ふつうのHaskellプログラミング , p.173) 普段よく見る「リストによるパターンマッチ」、

(x:xs)

エラトステネスのふるい」でも、リストの先頭要素 p と、先頭以外のリスト ps にパターンマッチさせている。

        sieve (p:ps) =
          p : sieve [n|n <- ps, n ‘mod‘ p /= 0]

こういうのがリストのパターンマッチだと最初頭に入れてしまったので、それ以外の方法が頭の隅から追いやられてしまっていた。 (@_@;) それに気づかされたのが、Haskell (programming language) - Wikipedia, the free encyclopedia に書かれていた「逆ポーランド記法」を計算するための関数の例。

calc :: String -> [Float]
calc = foldl f [] . words
  where 
    f (x:y:zs) "+" = y+x:zs
    f (x:y:zs) "-" = y-x:zs
    f (x:y:zs) "*" = y*x:zs
    f (x:y:zs) "/" = y/x:zs
    f xs y = read y : xs

(Haskell (programming language) - Wikipedia, the free encyclopedia より)

(cf. Python で逆ポーランド記法の計算)

 

リストのデータコンストラクタ

これを理解する前に、まずはリストについて調べておく。 Haskell においてリストは特別な立場にある。

The Haskell 98 Report: 定義ずみの型とクラス によると、

data  [a]  =  [] | a : [a]  deriving (Eq, Ord)

リストは、二つの構築子をもつ代数的データ型であるが、3.7 節で述べたように特別な構文をもつ。最初 の構築子は空リストで、`[]' (「ニル」)と書く。二つ目の構築子 は、`:' (「コンス」)と書く。

「コンス」というのは `constructor’ から来ている。以下を参照。

 

上記の引用にあるように : , [] は特別な構文。他のデータコンストラクタとは違う形をしている。そのため、関数における引数のパターンマッチにおいて、「これはデータコンストラクタによるパターンマッチなんだ。」という意識が薄れてしまっていた。

先ほどの逆ポーランド記法の計算に戻る。where 節の中の f 関数の引数を見ると、

(x:y:zs)

最初、「あれ?何で 3 つもあるの?」と思ってしまった。 ^^;

結局これもデータコンストラクタなんだから、以下のようにリストを生成したときのコンストラクタの使い方と対応している。

Prelude> 1:2:[3..10]
[1,2,3,4,5,6,7,8,9,10]

パターンマッチの視点から見ると、リストを次のようにバラしている。

  • 先頭の要素
  • 先頭の次の要素
  • 残りの要素のリスト

 

foldl

fold についても復習。(cf. Haskell の foldl , foldr)

型、要素を足し合わせていく方法、要素をリストにまとめていく方法について確認。

Prelude> :t foldl
foldl :: (a -> b -> a) -> a -> [b] -> a
Prelude> foldl (+) 100 [1..10]
155
Prelude> foldl (\x y -> x ++ [y]) [] [1..10]
[1,2,3,4,5,6,7,8,9,10]

これを前提として、先ほどの逆ポーランド法の計算を見ていく。例えば、次のように calc を呼出したとすると、

calc = "1 2 + 3 *"
calc = foldl f [] ["1","2","+","3","*"]

関数 f は先ほど foldl の型で見たように、二つの引数を取るので、最初は…

あれ? イメージできない。パタッ(o_ _)o~†

 

foldl の動作を手動で

仕方がないので、calc 関数をばらして、f を独立した関数に変更し、動作を試すことに。

calc :: String -> [Float]
calc = foldl f [] . words

f (x:y:zs) "+" = y+x:zs
f (x:y:zs) "-" = y-x:zs
f (x:y:zs) "*" = y*x:zs
f (x:y:zs) "/" = y/x:zs
f xs y = read y : xs

これをファイルに保存して、GHCi 上で :l によりロード。:t f で関数 f の型を見ると、

f :: (Fractional a, Read a) => [a] -> [Char] -> [a]

以下の計算を手動で試してみる。

*Main> calc "1 2 3 4 5 / - * +"
[5.4]

f 関数の呼出しを繰り返し…

*Main> f [] "1"
[1.0]
*Main> f [1.0] "2"
[2.0,1.0]
*Main> f [2.0,1.0] "3"
[3.0,2.0,1.0]
*Main> f [3.0,2.0,1.0] "4"
[4.0,3.0,2.0,1.0]
*Main> f [4.0,3.0,2.0,1.0] "5"
[5.0,4.0,3.0,2.0,1.0]
*Main> f [5.0,4.0,3.0,2.0,1.0] "/"
[0.8,3.0,2.0,1.0]
*Main> f [0.8,3.0,2.0,1.0] "-"
[2.2,2.0,1.0]
*Main> f [2.2,2.0,1.0] "*"
[4.4,1.0]
*Main> f [4.4,1.0] "+"
[5.4]

あ~ (@_@) なるほど、そういうことか。

再掲。

calc :: String -> [Float]
calc = foldl f [] . words
  where 
    f (x:y:zs) "+" = y+x:zs
    f (x:y:zs) "-" = y-x:zs
    f (x:y:zs) "*" = y*x:zs
    f (x:y:zs) "/" = y/x:zs
    f xs y = read y : xs

素朴にエラトステネスのふるい (3) – Python の実装を Haskell へ

素朴にエラトステネスのふるい (2)」 の続き。

前回、再帰的な表現を関数からオブジェクトのメソッドに移すことにより、再帰関数のイメージを複数のオブジェクトの連鎖として理解しようと試みた。実際にオブジェクトが連鎖する場合の実装を Python でしてみたが、今回はこのイメージに沿うように Haskell のコードを変更してみる。

問題はオブジェクト指向的な視点を関数型へ持ち込むとき、どのようにして考えたらいいのだろうか … (@_@;)

 

状態がない

ところで、関数型言語に触れてみたいと思い、Haskell をいじりはじめて最初に驚いたのは、

純粋関数型言語はチューリングマシンをベースとしたCJavaなどの手続き型言語と違い、状態という概念をもたない。そのため参照透過性が保たれる。

(関数型言語 – Wikipedia より)

え… (@_@;) 状態なくてどうやってプログラミングをするんだろ?と。どうしてもプログラミングというと、内部的に状態を持った小人さんたちが、インターフェイスを通してコラボレーションすることにより、

わぁい♪ヽ(▽ ̄ )ノ/(_△_)ヽ(  ̄▽)ノわぁい♪

と計算を進めていくというイメージが強いので、「状態がない」ということに違和感を感じた。

 

Referential transparency

ふつうのHaskellプログラミング (p.113) の「参照透明性」のコラムには次のように書かれている。

… 呼出すごとに返り値の違うメソッドが作れるのは、Java に (再) 代入があるからです。言語に (再) 代入があると、ある式を、それと等しい値で置き換えられなくなります。つまり、参照透明でなくなります。

再代入についての話は、本の冒頭から書かれており、

… 代入を排除することでプログラムは明解に、わかりやすくなえります。… なぜグローバル変数を濫用すると読みにくくなるかと言えば、それは、その変数がいつ変更されるかわからないためです。(p8)

Real World Haskell においてもその冒頭で、Why functional programming? Why Haskell? として、

In Haskell, we de-emphasise code that modifies data. Instead, we focus on functions that take immutable values as input and produce new values as output. Given the same inputs, these functions always return the same results. This is a core idea behind functional programming.

(太字は引用者による)

「変更不能な値を入力として受けとり、新しい値を出力する」という関数型言語としての特徴が述べられている。

 

変数のスコープ

ちなみに、なぜそのような方法を採用したかということについて、「ふつうのHaskellプログラミング 」ではグローバル変数を例に挙げて説明している。つまり、「グローバル変数の弊害から逃れる方法」には二つの方法があると。一つは「変数のスコープをできるだけ小さくして、変数が変更される場所を制限すること」(p.8) 。それからもう一つは「変数の値を変更できないくしてしまうこと」。前者はその通りだと思えても、後者の選択は何て大胆なんだとびっくりした。

考えてみると、前者は当然ながらグローバル変数に限った話ではない。インスタンス変数然り、ローカル変数もまた然り。例えばもし、巨大なクラスがあり、そこにたくさんのインスタンス変数があって、複数のメソッドからいじられているコードを見たら、変更する気が失せる。(+_+) 加えて、テストコードが付属してないなら最悪で、そのままふたをして地中深くに埋めておきたくなってしまう。^^; ローカル変数にしても同じ。「リファクタリング」の「問合わせによる一時変数の置き換え」の対象くらいのものだったらいいけれど、複数の仕事を長々と、 for や if のネスト祭りで、一画面以上に渡り変数がボカスカとひっかき回されているメソッドを見たら、脳みそ沸騰、そして卒倒。(o_ _)o~† インスタンス変数と呼んではいるけれど、人の認識にとってグローバル変数と等価なものは本当に勘弁してほしい。

正直、自分の容量の小さい脳みそでは、成長過程にあるクラスにおいてインスタンス変数が画面から徐々に見えない位置になるに従って徐々にやばくなる。複数のメソッドからインスタンス変数に変更を加えたり、状態に応じたコードを書く段になると目が回りだし、テストコードを書きながらでないとエラー連発。ストレス爆発。昔、「リファクタリング」を読んでいなかったらと思うと、ぞっとする。(@_@;) 長いメソッドや、巨大なクラスを書いて平気な人は、きっと記憶力がいいか、もしくは頭がいいのだろうと思う。自分のように、昨日書いたコードの内容なんて忘れてしまうタイプの脳みそでは全く無理。^^; そういった意味で、もしかすると Haskell が書けるようになったら楽できるかも、という密かな期待が…。

そうえいば、Haskell をさわっていて、はじめに連想したのがリファクタリングだった。メソッド一つの意味を明確にするという動機が、結果的にあらゆるローカル変数・インスタンス変数のスコープを小さくすることに貢献し、問題の局所化と変更への耐性を上げていく。コンパクトでシンプルなものは、理解しやすいし変更しやすい。Haskell に慣れて書けるようになったら、自然とそういたったコードに落ち着くのかな (?) とか思いつつ、未だコンパクトな再帰で目を回しているので、先は長い… ^^;

 

値をまるごと更新

JavaScript

さて、そんな状態を持たない関数型言語ではどのようにプログラミングをしていったらいいのだろう? 内部的な変数を操作する考え方にどっぷりとつかっているので、そんなことを突然言われても、うーん… (@_@;)

と、ここで以前に「まるごと JavaScript」 の「Flash の ActionScript で関数型風プログラミング」の記事に「関数型プログラミング」の説明が書かれていたことを思い出した。それによると、

関数がグローバル変数に頼らないようにする方法のひとつは、… 関数が遷移前の状態を引数として受け取り、関数の実行が終了したら新しい状態を返すというものです。 (p.183)

上記の次頁には「状態を受け取って返す関数」としてのカウンタの例があった。これを少しだけ変更して定義してみる。

function next_counter(counter){
    // 渡されたカウンタを新しいオブジェクトで置き換える
    return {state : counter.state + counter.step,
            step  : counter.step}
}

var counter = {state:0,       // カウンタの値 
               step:1}       // 増分

// カウンタの値を 10 進める
for (var i=0; i<10; i++){
    console.log(counter);
    counter = next_counter(counter);
}

counter 変数はカウンタを表現しており、内部でカウンタの値の状態(state) とカウンタの増分 (step) を持っている。next_counter 関数は、与えられたカウンタオブジェクトの値から新しくオブジェクトを作って返す。

これを Firefox の Firebug 上で実行すると結果は、

Object state=0 step=1
Object state=1 step=1
Object state=2 step=1
Object state=3 step=1
Object state=4 step=1
Object state=5 step=1
Object state=6 step=1
Object state=7 step=1
Object state=8 step=1
Object state=9 step=1
Object state=10 step=1

関数を呼出す度にオブジェクトが新しいオブジェクトに置き換えらているが、結果だけ見ると、あたかも状態を持っているかのように見える。

 

Java

これを見て更に思い出したのが、Joshua BlochEffective Java の「項目 13 不変性を選ぶ」という節。そこでは 複素数 (Complex) クラスにおける不変性について説明されていた。(p.62) 以下は Complex クラスにおける足し算の例。

	public Complex add(Complex c) {
		return new Complex(re + c.re, im + c.im);
	}

ここでは計算の結果を、渡された Complex オブジェクトの内部を変更して返すのではなく、新たなオブジェクトを生成して返している。

こういった実装の代表例としては、

イミュータブルなオブジェクトの古典的な例はJavaのStringクラスのインスタンスである。

String str = "ABC";
str.toLowerCase();

メソッドtoLowerCase()は変数strの値"ABC"を書き換えない。代わりに新しいStringオブジェクトがインスタンス化され、生成時に"abc"という値が与えられる。

(イミュータブル – Wikipedia より)

Effective Java (p.63) によると、

これは、関数的(functional)方法として知られています。なぜならば、メソッドは、そのオペランドを修正することなく、オペランドに対して関数を適用した結果を返すからです。…

不変オブジェクトは単純です。不変オブジェクトは、正確に一つの状態にあることができますし、それはオブジェクトが作られた時の状態です。もし、すべてのコンストラクタがクラスの不変式(invariant)を確立することを保証するならば、それらの不変式がずっと真のままであることが保証されますし、…

一方、可変オブジェクトは任意の複雑な状態空間を持つことになります。もし、ミューテーターメソッドにより行われる状態遷移の正確な記述を、ドキュメンテーションが提供していなければ、可変クラスを信頼して使用することは、困難か不可能になります。

(太字は引用者による)

これに加えて「項目 24 必要な場合には、防御的にコピーする」(p.114) も参考に。実装例は以下を参照。コンストラクタにおいて可変オブジェクトを受け取ったら、それを元にして新しくオブジェクトを生成してフィールドに設定。また、get メソッドにおいても、同様にクライアントに渡す前に新しくオブジェクトを生成して渡している。

まとめとして次のように述べられている。

可変にすべきかなり正当な理由がない限り、クラスは不変であるべきです。 (同上、p.67 より)

 

スタック の Haskell での実装例を読む

なるほど、中身をいじることができなければ、「渡されたオブジェクトの中身を元に新たに同じ型のオブジェクトを生成」して返せばいいというわけか。(@_@)

ふつうのHaskellプログラミング を見ると、 「9.2 代数的データ型」の「多相的な型の宣言」「再帰的な型」(p.232)、また、「フィールドの更新」(p.228) 辺り が参考になりそう。

 

スタック

Implementation of basic functions にはスタックの Haskell での実装例が書かれていた。

最初に、以下のような多相的な型をフィールドに持つ Stack a 型の定義がある。 (Type declaration より)

data Stack a = MakeStack [a]

push の実装は Implementation of basic functions に、

push :: Stack a -> a -> Stack a
push (MakeStack s) element = MakeStack (element:s)

なるほど、第一引数 Stack a 型の値を `オブジェクト’ 、関数名 push を `メソッド’ と見たてれば、クラスによる実装と同じように見える。ただし、違うのは、返される値が「引数を元に作られた新しい値」であるということ。

また、pop を見ると、返される値がタプルであれば、pop で取り出した値と、pop した後のスタックを返すことができることがわかる。言われてみれば当り前だけれど、「なるほど (@_@) こうすればいいのか」と思った。(同上より)

pop :: Stack a -> (a, Stack a)
pop (MakeStack (h:t)) = (h, MakeStack t)
pop (MakeStack []) = error "Attempt to pop an empty stack."

 

結局、中身を変更する変わりに、「中身が変更された状態を表わす新しい値」を作って返すということ。これでやっと実装できそうな感じになった。 ^^

 

実装

前回に書いたクラス図がこちら。これに沿って実装していく。

081026-002

081023-002

 

まずは、代数的データ型で構造を記述する。

data Sieve = Sieve {
      list :: [Int],    -- ふるいにかける前の数値のリスト
      tail :: Sieve   -- リストの先頭要素でふるいにかけた後、
                        --「このふるいから落ちた数値のリスト」を保持するふるい
    }
           | Empty  
             deriving Show

前回の Python の実装と違うところは、「ふるいにかけるときの基準となるリストの先頭要素」を保持するフィールド head がないこと。これは右図のピンク色の数値に相当するが、「ふるいにかける前のリスト」から取得できるので、わざわざ作る必要がないことに気がついた。

データコンストラクタ Empty は、Python のコンストラクタ内において、self.tail = None と設定していることに相当するものを書きたいため。

これだけ見ると Python におけるクラスの記述とほぼ同じであることがわかる。

 

sieveByHead 関数

次に、`一つ’ のふるいにおいて、「設定された数値のリスト」の `先頭の要素’ で残りの数値をふるいにかける動作を書く。

sieveByHead :: [Int] -> [Int]
sieveByHead (x:xs) = [n | n <- xs, n `mod` x /= 0 ]

これも素直にそのまま。

 

sieve 関数

次に、連続してふるいにかけていく動作を書いてみる。ここが Python とは異なるところ。先ほどのスタックの push の実装を真似して、「ふるいにかけたら、ふるい」が返ってくるようにする。当然ながらふるいにかける前とかけた後の中身(値)は異なる。ということで、関数の型宣言は以下のように。

sieve :: Sieve –> Sieve

定義を考えるときは、Python で定義したときのように考える。つまり、注目するのは右図の一番上のふるいと、そのふるいから落ちた数値のリストを拾う、すぐ下のふるいのみ。

 

最初にした型の定義より、はじめに数値をセットするふるいが以下のようであるとする。この場合、ふるいの一つに「2 からはじまる自然数のリスト」を設定。

Sieve [2..] Empty 

ここから下のふるいについて考えていく。 sieve 関数の左側が、上記の型宣言に合わせて以下のようであるとすると、

sieve (Sieve xs s) =

上のふるいから落ちた数値のリストは、先ほど定義した関数を利用して、

sieveByHead xs

これを元に下のふるいは生成されるので、下のふるいは以下のように表現できる。

Sieve (sieveByHead xs) s

 

今定義しているのは、ふるいを連続的にかけていく sieve 関数。しかし、注目するのは上二つのふるいのみでよい。なぜなら、 sieve 関数が一回に適用できるふるいは一つであり、適用された引数によって「ふるいにかける様子」を表現しなくてはならないため。

注目した二つのふるいの内、上のふるいにしてみれば、「連続的にふるいをかけろ!」と言われても、その全体の動作は知らず、自身は下にくるふるいに対して「今自分のところから落ちた数値を元にふるいにかけて!」とお願いするしかない。つまり、上のふるいから見ると、以下のように表現することがふるいを連続的にかけるときの動作の一端を担うことになる。

sieve $ Sieve (sieveByHead xs) s

 

では、最終的に何が返るのか?それはスタックにおいて、push したときのように sieve 関数をはじめに適用したふるいということになる。

Sieve xs (sieve $ Sieve (sieveByHead xs) s)

しかし、これだけでは関数の呼出しが延々と続いてしまう。そこで、ふるいの一番下の状態のときの様子を表現する必要がある。

sieve (Sieve [] _) = Empty

数値のリストが空になったら、「空ですよ」と返す。

 

sieve 関数全体を示すと、

sieve :: Sieve -> Sieve
sieve (Sieve [] _) = Empty
sieve (Sieve xs s) = Sieve xs (sieve $ Sieve (sieveByHead xs) s)
 
headSieve 関数

次に、連続的にふるいにかけた後、ふるいの基準となった数値のみをリストにして返さなくてはならない。上記の関数を適用した後に、ふるいの基準のみを抽出する関数を定義する。型宣言は、連続的にふるいをかけた後のふるいを与えると、数値のリストが返るようにする。関数名は headSieve とした。

headSieve :: Sieve -> [Int]

これも先ほどと同じように上二つのふるいのみで考える。リストを返すパターンなので (:) を使って、

headSieve (Sieve (x:_) s) = x : (headSieve s)

パターンマッチにおいて、与えられたふるいの持っている数値のリストの先頭要素のみを束縛。その後、そのふるいから見て、一つ下のふるいに対しても、同様に「持っているリストの内、先頭の要素のみを返して!」とお願いする。

今回もこのままでは、関数の呼出しが延々と続いてしまうので、空の場合を定義しないといけない。全体では以下のようになる。

headSieve :: Sieve -> [Int]
headSieve Empty           = []
headSieve (Sieve (x:_) s) = x : (headSieve s)

 

完成
-- ふるい
data Sieve = Sieve {
      list :: [Int],    -- ふるいにかける前の数値のリスト
      tail :: Sieve   -- リストの先頭要素でふるいにかけた後、
                        --「このふるいから落ちた数値のリスト」を保持するふるい
    }
           | Empty  
             deriving Show

-- リストの先頭要素でふるいにかける
sieveByHead :: [Int] -> [Int]
-- sieveByHead (x:xs) = [n | n <- xs, n `mod` x /= 0 ]
sieveByHead (x:xs)
    | x == 1 || x == -1 = [x]
    | otherwise         = [n | n <- xs, n `mod` x /= 0 ]

-- 連続してふるいにかける
sieve :: Sieve -> Sieve
sieve (Sieve [] _) = Empty
sieve (Sieve xs s) = Sieve xs (sieve $ Sieve (sieveByHead xs) s)

-- Sieve に設定されたリストの先頭要素を、すべての Sieve に渡って取得する
headSieve :: Sieve -> [Int]
headSieve Empty           = []
headSieve (Sieve (x:_) s) = x : (headSieve s)

main = do
  print $ take 10 $ headSieve $ sieve $ Sieve [2..] Empty 

sieveByHead 関数は、n = 1, –1 で無限ループになってしまったので、パターンマッチを増やした。

うーん、しかし、sieveByHead 関数は sieve の中に入れた方が読みやすいかなぁ…

sieve :: Sieve -> Sieve
sieve (Sieve [] _) = Empty
sieve (Sieve l@(x:xs) s) = Sieve l (sieve $ Sieve (sieveByHead x) s)
    where
      -- リストの先頭要素でふるいにかける
      sieveByHead x
          | x == 1 || x == -1 = [x]
          | otherwise         = [n | n <- xs, n `mod` x /= 0 ]

しかし、翌日になって上記を見ると、「あれ?これ何やってたんだっけ…」とか思う自分の脳みそに…

パタッ(o_ _)o~†

2008年10月28日火曜日

Haskell の fmap

A Gentle Introduction to Haskell: Classes によると、

fmap 関数は以前につかった map 関数を一般化したものです。

map と言えば、リストの各要素に対して関数を適用するための関数。例えば、リストの要素を 2 倍したいときは、

print $ map (* 2) $ [1,2,3,4,5]       -- [2,4,6,8,10]

map がリストのためにあるのに対し、fmap は自分で作成した代数的データ型に対して、map のように各要素に関数を適用するためにあるようだ。今回、以前に定義したシンプルな Iremono 型 を少し変更し、これに適用できるようにしてみる。

Iremono a 型の定義を以下のように変更。

data Iremono a = Iremono [a] deriving (Show,Eq)

前回は Iremono 型のフィールドが 型変数 a だったけれど、今回はリスト [a] にした。

 

fmap を使わずに

まずは、fmap を使わずに「入れ物」(Iremono a) の中の中身の要素を 2 倍にする関数を定義してみた。

と、その前に念のため確認。例えば次のように Iremono a 型に map 関数を適用してみても、

print $ map (*2) $ Iremono [1,2,3,4,5]

当然ながら怒られる。 (+_+)

    Couldn't match expected type `[a]'
           against inferred type `Iremono t'
    In the second argument of `($)', namely
        `Iremono [1, 2, 3, 4, ....]'
    In the second argument of `($)', namely
        `map ((*2)) $ Iremono [1, 2, 3, 4, ....]'
    In the expression: print $ map ((*2)) $ Iremono [1, 2, 3, 4, ....]

 

入れ物の中の要素を 2倍にする

Iremono の要素を 2 倍にする doubleIremono 関数を定義。

doubleIremono :: Num a => Iremono a -> Iremono a
doubleIremono (Iremono xs) = Iremono (map (*2) xs)

doubleIremono 関数を使ってみる。

print $ doubleIremono $ Iremono [1,2,3,4,5]  -- Iremono [2,4,6,8,10]

 

入れ物の中身の要素に関数を適用

doubleIremono 関数は「要素を 2 倍する」しかできないので、もう少し汎用的にしたい。そこで、関数を渡したら「入れ物」の中身に関数を適用する mapIremono 関数を定義。

mapIremono :: (a -> b) -> Iremono a -> Iremono b
mapIremono f (Iremono xs) = Iremono (map f xs)

これを使ってみると、例えば、

  print $ mapIremono (* 2) $ Iremono [1,2,3,4,5]
  print $ mapIremono (\x -> if x < 3 then "hoge" else "piyo") $ Iremono [1,2,3,4,5]

結果は、

Iremono [2,4,6,8,10]
Iremono ["hoge","hoge","piyo","piyo","piyo"]

 

A Gentle Introduction to Haskell の例を見る

最初に、Functor クラスの fmap 宣言を確認しておく。

fmap は、Functor クラスのメソッド。

class Functor f where
    fmap :: (a -> b) -> f a -> f b

A Gentle Introduction to Haskell: Classes では Tree a 型を Functor のインスタンスにする例が書かれている。Tree a 型は、

data Tree a    = Leaf a | Branch (Tree a) (Tree a)

(A Gentle Introduction to Haskell: Modules より)

これを以下のように Functor クラスのインスタンスにしている。

instance Functor Tree where
  fmap f (Leaf x)       = Leaf   (f x)
  fmap f (Branch t1 t2) = Branch (fmap f t1) (fmap f t2)

実装を見ると、各 Tree の要素に fmap 関数の第一引数である `f’ を適用しているのがわかる。

ここでいつも頭が混乱するのでゆっくり考える。(+_+)  上記のように型コンストラクタ Tree は Functor クラスのインスタンスであると宣言した。 Functor Tree の fmap の実装で返しているは Tree a 型。Functor クラスの定義の `f’ を Tree で置き換えることができるとすると、 次のよう想像することができる。

class Functor Tree where
    fmap :: (a -> b) -> Tree a -> Tree b

この fmap の型が Tree  a 型における実際の fmap の宣言ということ。だから、Tree  a 型の fmap が返している型が Tree b 型で OK と。

あ~ (@_@;) なかなか慣れない…。

 

Iremono a 型にも fmap

上記を真似して、fmap が適用されたとき、「入れ物」の中身である各要素に関数が適用されるように実装してみる。

instance Functor Iremono where
    fmap f (Iremono xs) = Iremono (map f xs) 

Tree のときと同じように Functor クラスの定義の f を Iremono で置き換えて考えてみると、

class Functor Iremono where
    fmap :: (a -> b) -> Iremono a -> Iremono b

上記で定義した fmap 関数の定義は、この宣言に沿うように定義されていることがわかる。

さて、実際に fmap を使ってみると、

  print $ fmap (*2) $ Iremono [1,2,3,4,5]   -- Iremono [2,4,6,8,10]

 

ところで、class Functor f where によると、

The Functor class is used for types that can be mapped over. Instances of Functor should satisfy the following laws:

 fmap id  ==  id
 fmap (f . g)  ==  fmap f . fmap g

上記を満たすように実装する必要があるらしい。第3回 mapからモナドを理解する:ITpro によると、

idは恒等関数(identity functionもしくはidentity)の略であり,入力したデータをそのまま返す関数です。(.)演算子は関数合成(functional composition)です。(.)の定義は,(f . g) x = f (g x)です。

確かに、型クラスによる宣言だけでは、関数がどのように振舞えばいいのかを規定することはできない。定義の内容はプログラマの手にゆだねられている。

「自分で定義した代数的データ型のフィールドの各要素に関数を適用していく」ということが、上記を保証することでなぜ実現できるのかという一番肝心なところがわからないのだけれど ^^; 、とりあえず一例だけでも試してみることに。  QQQ

main = do
  print $ test1   # True
  print $ test2   # True

test1 = (fmap id $ Iremono [1,2,3,4,5]) == (id $ Iremono [1,2,3,4,5])
    where id = (\x -> x)
test2 = (fmap (f . g) $ Iremono [1,2,3,4,5]) == (fmap f . fmap g $ Iremono [1,2,3,4,5])
    where
      f = (* 2)
      g = (+ 100)

2008年10月26日日曜日

Haskell で文字列の分割と結合

文字列をリストへ分割

改行で区切るなら、

lines :: String -> [String]

空白で区切るなら、

words :: String -> [String]

特定の文字列で区切るなら正規表現を使って、( Text.Regex )

splitRegex :: Regex -> String -> [String]

import Text.Regex

main = do
  print $ lines "hoge\npiyo\nfuga"
  print $ words "hoge piyo fuga"

  print $ splitRegex (mkRegex ",") "hoge,piyo,fuga"
  print $ splitRegex (mkRegex "--") "hoge--piyo--fuga"

 

リストを文字列へ戻す

lines, words の接頭辞として `un’ をつける。

文字列でくっつけたい場合は、intercalate 。(cf. Haskell でリストの要素を join - List.Data の intersperse, intercalate)

import Data.List

css = ["hoge","piyo","fuga"]

main = do
  putStrLn $ unlines css
  putStrLn $ unwords css

  putStrLn $ intercalate "—" css

結果は、

hoge
piyo
fuga

hoge piyo fuga
hoge--piyo--fuga

単に文字列のリストをくっつけて、文字列にしたいだけなら concat 。文字列のリストは、文字のリストのリストであるため。

 

日本語

日本語を扱うときは、「Haskell で日本語表示 - utf8-string を利用して」 を参照。

 

関連記事

Meadow (Emacs) で矩形領域をカット & ペースト

C-x r k → C-x r y

微妙に使わないものは何度でも忘れてしまう。 (+_+) どうやって忘れないようにしようか。

C-k → C-y

このカット&ペーストのパターンはさすがに忘れないのでこれを基本形と考え、

C-x

は、コマンド実行 command e`x’ecute 。そして矩形の`r’ectangle 。

… いつまで覚えていることができるかな。

 

その他

最近やっと M-x goto-line と入力しなくても、M-g g で特定の行へジャンプする入力を手が覚えてくれた。 ^^; これは Go! Go! と覚えた。

 

参考サイト

素朴にエラトステネスのふるい (2) - オブジェクト指向を手がかりに再帰表現を考える

素朴にエラトステネスのふるい (1)」の続き。

コードを再掲。Haskell では、

sieve (p:ps) =
  p : sieve [n|n <- ps, n ‘mod‘ p /= 0]

Python だと、

def sieve(L):
    if not L: return []
    else:
        return [L[0]] + sieve([x for x in L[1:] if x % L[0] != 0])

やはり、今日見ても再帰的な表現はどこか理解しきれないと感じる。(+_+) これ以上考えてもらちが開かないので、このままの形で考えるのは諦めた。

 

ふるいの動作

ところで、上記のコードは直接素数を生成するわけではない。`ふるい’ はあくまでも素数を生成するためのツール。「2 からはじまる自然数のリスト」を与えたときに素数のリストが生成される。

main = do print $ take 10 $ sieve [2..]
print sieve(range(2,100))

もし、「1 からはじまる自然数のリスト」を引数に与えたなら、その `ふるい’ の性質上、生成されるのは [1] 。なぜなら、最初の「リストの先頭要素で割り切れる要素をふるいにかける」というところで、1 以降の要素が脱落してしまうため。つまり、上記のコードは `ふるい’ の動作を表現しているということを忘れてはダメ。素数が生成されるのはその結果に過ぎない。

 

ふるいのイメージ

081023-002よくわからないときは絵を描くことにしているので、素数を生成するときの `ふるい’ をイメージしてみた …

「このふるいは、近所の量販店で売られているふるいとは違う。小麦粉を `ふるう’ のではなく、「数値のリスト」をふるう。動作も一風変わっていて、数値のリストをザザーっと流し込むと、最初にふるいに落ちた数値がふるいにセットされ、その数値で『割り切れない数値』がふるいを通り抜け、割り切れた数値がふるいに残される。

ある数値のリストをこのふるいにかけたとする。直後、ふるわれた数値が地面に落ちる寸前、同じ種類のふるいを別にもう一つ用意し、落ちていく数値を受けとめる。同種のふるいなので動作も同じ。また落ちてきた先頭の要素がふるいにセットされ、残りの要素に対してふるいをかける基準となる。

手元にふるいが更に何個もあったので、これをどんどん繰り返していたら、最後には落ちていく数値がなくなってしまった。」

 

ふるいのふるまい

この様子を示したの右図。で、このふるいを一つのオブジェクトと見たて、ふるいクラスを作成する。まずはこのふるいに対して何ができるかという視点で。

  • 数値のリストをセットする
  • ふるいにかける

ここからふるいの内部はどうなっているかと想像すると、

  • セットされた数値のリストを持つ
  • セットされたリストの先頭要素を持つ

それから、右図のようにふるいをつなげて使っている様子をふるい自身に覚えてもらうことにすると、

  • 自分がふるい落とした数値を受けとめるふるいを知っている

そして、各自ふるった後、落ちた数値を受け止めるためのふるいを自分で用意してもらうことに。

 

クラス図

081026-002

 

実装

class Sieve:
    """ ふるい

    このクラスのオブジェクトが連なって働くことによって
    「エラトステネスのふるい」の振る舞いをする
    
    - 使い方
    1. インスタンスの生成
    2. 値の設定
    3. ふるいにかける
    """
    def __init__(self):
        self.L = []         # ふるいにかける前の数値のリスト
        self.head = None    # ふるいにかける数値のリストの先頭 (ふるいの基準)
        self.tail = None    # Sieve : ふるいにかけた後の数値のリストを保持

    def set(self,L):
        """ ふるいにかける数値のリストを設定 """
        self.L = L
        self.head = L[0]
        return self

    def sieve(self):
        """ ふるいにかける """
        self._sieveByHead()
        if self.tail:
            self.tail.sieve()

    def _sieveByHead(self):
        """ 設定されたリストの要素をその先頭要素で割り、
        割り切れなかったものをふるいから落とし、新たにふるいに設定する

        ※ 設定されたリストの要素が二つ以上ないと tail は None のまま。
        """
        if len(self.L) > 1:
            self.tail = Sieve()
            sieved = [x for x in self.L if x % self.head != 0]
            if sieved:
                self.tail.set(sieved)

    def __str__(self):
        """ ふるいの基準として使われた数値を出力 """
        if not self.head: return ""
        return str(self.head) + ", " + str(self.tail) if self.tail \
            else str(self.head)

s = Sieve().set(range(2,100))
s.sieve()
print s

最初に示したコードと比べるとかなり長くなってしまったが、こちらはイメージしやすい。 ^^ なぜかと言えば、クラス定義で意識するのはオブジェクト単位であり、オブジェクト一つで何ができるかを考えればいいため。sieve メソッドを見ると「自らふるい落とした数値のリストを受けとる`ふるい’」に対して sieve メソッドを呼出している。しかし、メソッドを見ても頭が混乱することはない。単純に `ふるい’ が `ふるい’ に対して 「sieve してくれ」と依頼しているようにしか見えないから。

結局、再帰的な関数において呼出す度に生成される文脈を、オブジェクトというイメージしやすい形に置き換えることにより理解しやすくなった気がする。オブジェクト指向で考えるときの一番基本的な戦略は、「オブジェクトと関わりのあるオブジェクトに対してどのようにコラボレーションするか?」を問うこと。この方針が `考える範囲を限る’ ということに対して、`オブジェクト‘ という範囲の枠を提供してくれる。

再帰的な関数との違いがもう一つ。それは、やることを分割しているということ。上記のクラスでの実装は、「ふるいにかける動作、ふるう動作、ふるいを連ねる動作、その結果を表示するための動作」を別々にした。そして、それらの動作を `一つのふるい’ というイメージと関連付けて考えた。再帰的な関数では、これを一行で表現する。そうすると、自分の脳みそでは、「一体誰が何をしてその結果どうなったの?」というところで、えっ?(@_@;)? となってしまう。

 

オブジェクト指向をアナロジーとして用いる

逆に言えば、再帰的な関数を、アナロジーとして上記のような表現とほぼ等価であると見なすことができれば、すんなりと理解できるはず…。明らかな動作の違いは、クラスによる実装では計算が終った時点で計算過程の一部の履歴が残っているのに対して、再帰関数では消えているという点くらいだし。… ということは、えーと、再帰関数である sieve を見たら、次のように見たてればいいということか。

  1. sieve というメソッドを持つオブジェクト」があると想定。(ふるいに相当)
  2. sieve を再帰的に呼出すところは、新しく同種のオブジェクトを生成すると見なす。
  3. ただし、そのオブジェクトは最終的に計算の過程を保持することなく、計算の終了とともに消滅して結果 (関数の返り値) のみを残す
  4. 最後に、残された結果を連結。

081026-001

つまり、関数で呼出された文脈をオブジェクトと見なし、そこで使われているローカル変数をインスタンス変数に見たてるということ。以前より少し理解が進んだような気になれた。 ^^


関連記事

2008年10月25日土曜日

Haskell で簡単なテキスト処理 (2)

Haskell で簡単なテキスト処理 - データの抽出」の続き。もう一度、前回と同じ処理を書いてみる。

対象のデータは、「Python で簡単なテキスト処理 (2) - データの抽出」で使った以下の 気象データ 。ここから、気圧 (1列目) と気温 (6列目) のデータのみを抽出したい。

1
998.7 1003.1 -- -- -- 6.0 10.9 1.8 36 16 2.6 4.8 北西 9.4 北西 6.9 -- -- 晴後一時曇 快晴
2
1007.2 1011.7 -- -- -- 6.2 11.2 1.1 35 19 3.1 6.6 北西 13.3 北西 9.1 -- -- 快晴 快晴
3
1011.6 1016.1 -- -- -- 5.9 10.4 1.5 45 29 1.8 3.6 北北西 6.4 南南東 9.1 -- -- 晴 快晴
4
1014.6 1019.1 -- -- -- 7.0 12.1 2.7 43 24 2.5 5.0 北西 9.4 北北西 9.0 -- -- 快晴 晴後曇
5
1014.0 1018.6 0.0 0.0 0.0 6.0 8.7 4.0 53 39 2.0 3.8 北西 6.1 西北西 3.7 --

※データファイルは UTF-8 で保存。

 

例1

まずは、なるべくシンプルに書くにはどうしたらいいかという視点で考えると、

import qualified System.IO.UTF8 as U
import Text.Regex.Posix

main = U.getContents >>= U.putStr . unlines . extract . lines
extract = map col . filter (\x -> not $ x =~ "^[[:digit:]]{1,2}$|^$")
col line = let ds = words line 
           in ds !! 0 ++ "\t" ++ ds !! 5

さすがに Ruby のようなシンプル書き方はできないかなぁ…。

追記(2008.11.7) : filter の第1引数を関数合成に変更するなら、

extract = map col . filter (not . (=~ "^[[:digit:]]{1,2}$|^$"))

 

IO モナド

前回と違うところは、main において do 式を使わずに (>>=) で置き換えたこと。 (>>=) は「左の箱の中身を、右の箱の中に流し込むための装置」と理解している。(これが正しいのかよくわからないのだけれど…。)  do 式では渡された中身を変数に束縛していたが、(>>=) を使うと省略できる。

※ モナドについてはまだよくわからない。(+_+) ただし、Meet the Monads の以下の部分を読んで、イメージとして何か「入れ物」と「中身」の扱いについてのメタ的な記述だというくらいの認識にはなった。 ^^;

多相型は多くの異る型の値を保持できるコンテナのようなものです。… Haskell ではこのコンテナの型も多相にすることができます。それゆえ、「m a」と書いて、ある型の値を保持するある型のコンテナを表現することができます。

 

Meet the Monads によると (>>=) の型は、

(>>=) :: m a -> (a -> m b) -> m b

関数の合成は、(cf. Haskell の関数合成)

(.) :: (b –> c) –> (a –> b) –> (a –>c)

getContens の型は、Prelude によると、

getContents :: IO String

これは、`IO’ という入れ物に String を入れて運び、 (>>=) で右側に中身だけ「よっこらせ」と降ろすということ。(多分)

lines は String が引数で、`IO’ の中身はここに投入される。

lines :: String -> [String]

そして、必要な処理がされると、putStr によってまた`IO’ に入れられて出力。これが main 関数の返り値になる。

putStr :: String -> IO ()

 

ところで、(.) により putStr から lines までが、Stirng –> IO() という型の一つの関数に合成される。

IO モナド によると、

Haskell では、トップレベルの main 関数の型は IO () でなければなりません。

(>>=) の関数の型を今回の場合に当てはめると、

IO String –> (String –> IO ()) –> IO ()

これで String が投入されると IO () が返され、main 関数の型に沿うようになる。

モナドついてわからないけれど、外見は上記のような理解でいいのだろうか… ^^;

 

正規表現

Haskell で正規表現 (2) =~ , Python・Ruby と比較しながら を参照。

数字を表わすには `\d’ ではなく、Rx - Posix Basic Regular Expressions によると、

[[:digit:]]

正規表現に一致しないものを抽出したい場合、not 関数 で Bool 値を反転させる。

not :: Bool -> Bool

 

リスト内包表記

map と filter を合成している関数をリスト内包表記に置き換えると、 

extract css = [col cs| cs <- [cs| cs <- css, not $ cs =~ "^[[:digit:]]{1,2}$|^$"]]   

しかし、あまり見やすくならないので map, filter の合成を使うことに。

map f . filter match

一つのパターンとして覚えておくといいかも。

 

例2

main 関数において、行を抽出した後に、列を抽出するという意図を明確にしたいなら、

import qualified System.IO.UTF8 as U
import Text.Regex.Posix

main = U.getContents >>= U.putStr . col . row
row = filter (\x -> not $ x =~ "^[[:digit:]]{1,2}$|^$") . lines
col = unlines . map col'
    where col' line = let ds = words line
                      in ds !! 0 ++ "\t" ++ ds !! 5 

line – unlines の組み合わせを別々の関数の入口と出口に配置し、filter ・ map をそれぞれ行・列の抽出処理に対応させるようにしてみた。例1.と比べて、どちらが理解しやすく書きやすいだろう?

filter に適用する無名関数が長いと感じたら、filter match として、match 関数を作成。

match cs = not $ cs =~ "^[[:digit:]]{1,2}$|^$"

 

例3

なるべくシンプルにしたいと思って、列の抽出をまとめていったら、逆に長くなってしまった。(+_+) 抽出する列数が少なければあまり意味がなさそう。

import qualified System.IO.UTF8 as U
import Text.Regex.Posix
import Data.List

main = U.getContents >>= U.putStr . unlines . row . lines
row = map col . filter match
match cs = not $ cs =~ "^[[:digit:]]{1,2}$|^$"
col cs = intercalate "\t" $ col' [0,5] cs
col' [] _ = [""]
col' (x:xs) cs = (col'' x) : (col' xs cs)
    where
      col'' i = (words cs) !! i

(cf. Haskell でリストの要素を join - List.Data の intersperse, intercalate)

 

例4

データを「表」とみなし、表は「列のリスト」からなり、列は「文字列のリスト」からなると考えると、

import qualified System.IO.UTF8 as U
import Data.List

data Table = Table [Row] 
instance Show Table where
    show (Table xs) = concatMap show xs

data Row = Row [String]
instance Show Row where
    show (Row xs) = intercalate "\t" xs ++ "\n"

main = U.getContents >>= U.print . filterCol . filterRow . readTable

filterRow :: Table -> Table
filterRow (Table rows) = Table $ filter match rows
    where
      match :: Row -> Bool
      match (Row cells) = if length cells > 1 then True else False

filterCol :: Table -> Table
filterCol (Table rows) = Table $ map (col [0,5]) rows
    where
      col :: [Int] -> Row ->  Row
      col is (Row cells) = Row $ map (cells !!) is

readTable :: String -> Table
readTable css = Table $ map readRow $ lines css

readRow :: String -> Row
readRow cs = Row $ words cs

データを readTable , readRow 関数によって Table 型の値に一度変換し、filterRow, filterCol によって必要な部分を抽出するようにした。そのため、ここでは正規表現を利用していない。

各関数はシンプルになったけれど、これはちょっと長すぎる気が。うーん… (+_+)

それにしても、こういう文字列から値の変換は、8.3 Read クラスと Show クラス のように Read を使うのかな?

 

感想

実際に関数を定義してみて実感したことがあった。それは、パターンマッチの書き方、つまり、フィールドの値を変数に束縛する仕方が、定義でどのように値を使いたいかを暗示しているということ。例えば、上記の filterRow 関数において (Row cells) としているところを (Row (x:xs)) と束縛していたなら、パターンマッチを見ただけで定義において再帰的に処理がされているのでは (?) と予想できる。普通のリスト処理のときはそんなの当り前と思っていたけれど、フィールドにリストがある型に対してはそういう見方になってなかった。 ^^; 多分、読むだけでアップアップしてた。


関連記事

2008年10月24日金曜日

Haskell で正規表現 (2) =~ , Python・Ruby と比較しながら

以前試したように Haskell において正規表現を使いたい場合は、Text.Regex モジュールを利用する。(cf. Haskell で正規表現)

流れとしては、

  1. 「正規表現型の値」を生成 : mkRegex
  2. (1) を利用して一致する部分があるか確かめる : matchRegex 
import Text.Regex
main = do 
    print $ matchRegex (mkRegex "(piyo)") "123hogepiyofuga"
    print $ matchRegexAll (mkRegex "(piyo)") "123hogepiyofuga"

結果は、

Just ["piyo"]
Just ("123hoge","piyo","fuga",["piyo"])

ただし、matchRegex において Just で包んで返される値は、 mkRegex の正規表現で subexpression として指定 したもの。

 

Python と比較

Python でも同様の手順を踏む。

  1. compile
  2. match または search (cf. 4.2.2 マッチング vs 検索)
import re
print re.compile("piyo").match("123hogepiyofuga")
print re.compile("piyo").search("123hogepiyofuga")

結果、

None
<_sre.SRE_Match object at 0x025F6598>

 

search と match の違い

4.2.3 モジュール コンテンツ によると、

search(pattern, string[, flags])

string全体を走査して、…

match(pattern, string[, flags])

もし string の先頭で0 個以上の文字が正規表現 pattern と マッチすれば、…

Haskell の matchRegex は search に相当するということかな。

 

compile なし

上記は正規表現を使い回さないのであれば、compile せずに、

print re.match("piyo", "123hogepiyofuga")
print re.search("piyo","123hogepiyofuga")

 

matchRegexAll のように

Haskell の matchRegexAll と同じものを取得するには、4.2.5 MatchObject オブジェクト を使い、

str = "123hogepiyofuga"
m = re.search("(piyo)",str)
print str[:m.start()], m.group(1), str[m.end():], m.groups()

結果は、

123hoge piyo fuga ('piyo',)

 

Ruby と比較

Ruby で正規表現と言えばすぐに /正規表現/=~ “文字列” を思い出す。(cf. Regexp - Rubyリファレンスマニュアル) そういう書き方ができるから、テキスト処理をするとき Ruby は扱いやすいなぁ~と思った。組み込み変数 は忘れてしまうけれど。 ^^;

p /(piyo)/ =~ "123hogepiyofuga"
p $`, $1, $', $~.to_a

結果、

7
"123hoge"
"piyo"
"fuga"
["piyo", "piyo"]

せっかくなので、上記の組み込み変数だけでも覚えておこう。

`~’

Python でも =~ が使えるといいのに…。 (+_+)

 

Haskell でも =~

Text.Regex.Posix.Wrap

前回、A Haskell regular expression tutorial を読むのを見逃してた。(via Cookbook - HaskellWiki) Haskell でも Ruby と同じように =~ が使えるようだ。

定義されている場所は、Text.Regex の一つ下の階層に Text.Regex.Posix があり、そのまた下の Text.Regex.Posix.Wrap。どういうモジュールかと思って見てみると、

WrapPosix.hsc exports a wrapped version of the ffi imports.

FFIとは - はてなキーワード 

ある言語から他言語で作成したライブラリを呼び出すインターフェイスで、ラッパーを作らずに直接呼び出す事が特徴である。

…と言われても、何に対してどうなのか具体的によくわからなかった。 ^^;

 

=~ の型

それはさておき、 =~ を見ると、

(=~) :: (RegexMaker Regex CompOption ExecOption source, RegexContext Regex source1 target) => source1 -> source -> target

うわっ!(@_@;) この型クラスの制約はどうやって読めばいいのだろうか? QQQ

A Haskell regular expression tutorial によると、とりあえずはシンプルに考えておけと。

this function is polymorphic in both its arguments and its return type; … Here’s a simplified type signature, to give you the idea.

  (=~) :: string -> pattern -> result

(I’ve left out the constraints on these type variables. …

なるほど。しかし、引数だけではなく、返り値の型まで多相とは…?

 

使い方

とりあえず、上記にならって使い方を試してみる。

import Text.Regex
import Text.Regex.Posix
main = do 
  print $ ("123hogepiyofuga" =~ "(piyo)" :: Bool)       # True

最後の :: Bool と型を指定するのがポイント。

最初にマッチした文字列を返すには、

  print $ ("123hogepiyofuga" =~ "(piyo)" :: String)     # "piyo"

マッチした全ての文字列をリストにして返すには、

  print $ ("123hogepiyofuga" =~ "(piyo)" :: [String])    # ["piyo"]

マッチした部分よりも前、マッチした文字列、マッチした文字列の後ろを返したい場合は、

  print $ ("123hogepiyofuga" =~ "(piyo)" :: (String,String,String))   # ("123hoge","piyo","fuga")

上記に加えて、先ほどのように部分式にマッチした文字列をリストにしたものも加えたい場合は、

  print $ ("123hogepiyofuga" =~ "(piyo)" :: (String,String,String,[String]))  # ("123hoge","piyo","fuga",["piyo"])

マッチした文字列の位置とその長さを得たい場合は、Text.Regex.Base をインポートしておいた上で、

  print $ ("123hogepiyofuga" =~ "(piyo)" :: (MatchOffset,MatchLength))  # (7,4)

2008年10月23日木曜日

素朴にエラトステネスのふるい (1)

Haskell プログラミング ~ 純粋関数型言語への誘い~ を読んでいたら、「エラトステネスのふるい」 (p15) というのがあった。

sieve (p:ps) =
  p : sieve [n|n <- ps, n ‘mod‘ p /= 0]

Python で真似するなら、

def sieve(L):
    if not L: return []
    else:
        return [L[0]] + sieve([x for x in L[1:] if  x % L[0] != 0])

(ただし、L は有限のリスト)

 

素数

エラトステネスの篩 – Wikipedia によると、

素数判定法の一種で、指定された整数以下の全ての素数を発見するための単純なアルゴリズムである。

え~ (@_@;) 、こんなシンプルな表現で素数が得られるとは…。てゆうか「素数」なんて言葉何年ぶりに聞いただろうか。 ^^;

ところで、素数とは、

1とその数自身以外に正の約数がない(つまり1とその数以外のどんな自然数によっても割り切れない)、1 より大きな自然数のこと。

(素数 – Wikipedia より)

 

再帰的な定義

上記 sieve 関数は、定義の中で更に sieve 関数を適用している。苦手な再帰的な表現だ。うーむ。。。 (@_@;)

素数のリストが欲しいときは、2 以上の自然数のリストに関数を適用。これにより素数の無限リストが得られる。

primes = sieve [2..]  (同上より)

Python であれば、range(2,100) などのような有限のリストを sieve 関数に渡す。

 

部分的に考える

どのように動作するか具体的な例で考えてみる。例えば、「2 ~ 11 の自然数のリスト」に sieve 関数を適用した場合は、

sieve [2..11] = 2 : sieve [n|n <- [3..11], n `mod` 2 /= 0]

あ~  (+_+) 、再帰はいきなり見ると目が回るので部分ごとに。

まずは、リスト内包表記から。

[n|n <- [3..11], n `mod` 2 /= 0]

sieve 関数に適用したのは [2..11] のリスト。だから、ここでは「seive に適用したリストの先頭以外の要素」の中で「seive に適用したリストの先頭である 2」 で割り切れない要素をリストにしている。これが `ふるい’ にかける様子の一部を表現。

 081022-001

 

その後 `ふるい’ にかけたリストに対して更に sieve 関数を適用。ここが再帰的に関数を適用しているところで、目が回る原因。(@_@;)

sieve [n|n <- [3..11], n `mod` 2 /= 0]

081022-002

言葉にすれば、

「ふるいにかける方法は、ふるいにかけたものを、更にふるいにかけること」

… と日本語にしたところで、再帰のイメージがしやすくなるわけではないか。 ^^;

 

混乱の原因

再帰の混乱の元は、再帰的に連なる呼出しをイメージしようとすることにある気がする。 (+_+) あくまでも考えるのは、連なる呼出し全体ではなく、そのスナップショット的な表現。例えば、上記のように sieve [2..11] と適用された場合、その適用された文脈でのみ動作を考える。再帰のそのまた先まで考えない。

081022-003

 

具体的に、sieve[2..11] を定義するのであれば、定義は適用された引数 [2..11] に対してどのような作用をするのか考えるということ。あくまでも、一つ先の再帰を手持のコマで表現する。もう一度、以下を見ると、

sieve [n|n <- [3..11], n `mod` 2 /= 0]

再帰的に適用している対象  (リスト内包表記の中の変数) は、その文脈での「関数の適用対象」を使って表現している。

 

類似したパターン

定義の内容を続けて読んでいくと、次に、上記の再帰的な関数の適用によって生成されるリストと、先頭要素である `2’ をくっつけてリストを作成。ここでも同じく、 sieve の再帰的な適用の先の先は読まない。関数は「自分が欲しいものが既に得られた」と仮定し、「それを使ってどうしたいのか」という視点で考えると考えやすい。

2 : sieve [n|n <- [3..11], n `mod` 2 /= 0]

上記を言葉で表現するなら、

「ふるいにかける方法は、『ふるいにかけるリストの先頭』と『その先頭の要素では割り切れない要素に対して更にふるいをかけた結果』をつなげてリストにする。」

ポイントは、ふるいにかけられることによって「何」が残るのかということ。ここが自分はイメージしにくい。 (+_+) 関数が何を返し、それに対してどうしたいのか。再帰的な定義だと急に動作を想像しにくくなる気がする。

これを考えるには、関数の定義で再帰的に考えなくても明らかな部分に注目。上記の例ではリストの先頭要素である `2’ が手がかりとなる。

ところで、seive 関数の全体を眺めると、次のように再帰的に関数を適用している。

sieve リスト = リストの先頭要素 : sieve リストの先頭要素以外に対するリスト内包表記

こうやってみると全体の形が「n からはじまる数の無限リストを得る」再帰と類似しているのがわかる。

ints :: Int -> [Int]
ints n = n : ints (n+1)

ints 関数の実際の動作を考えると、例えば ints 1 なら、

1 : ints(2)
1 : 2 : ints(3)
1 : 2 : 3 : ints(4)
1 : 2 : 3 : 4 : ints(5)

再帰的に ints が適用されるごとに先頭要素がひねりだされ前の要素にくっついていくという感じがする。適用されるごとに ints がリストを生長させているようにも見える。

sieve 関数も同様に見たてることができる。 sieve により適用されたリストの先頭がひねりだされ、リストが生長していく。再帰的に sieve 関数に適用されるリストによって、最終的に生成されるリストの内容が決定される。つまり、sieve に適用するリスト内包表記による操作がリストの要素を構成する決め手。このとき、最初にひねりだされるのが、リストの先頭要素である自明な `2’ 。

 

実際に確かめる

しかし、やはり実際に関数が定義と置き換えられていく様子を追ってみないと納得できない。 ^^;

2 : sieve [n|n <- [3..11], n `mod` 2 /= 0]
2 : sieve [3,5,7,9,11]
2 : 3 : sieve [n|n <- [5,7,9,11], n `mod` 3 /= 0]
2 : 3 : sieve [5,7,11]
2 : 3 : 5 : sieve [n|n <- [7,11], n `mod` 5 /= 0]
2 : 3 : 5 : sieve [7,11]
2 : 3 : 5 : 7 : sieve [n|n <- [11], n `mod` 7 /= 0]
2 : 3 : 5 : 7 : sieve [11]
2 : 3 : 5 : 7 : 11 : sieve [n|n <- [], n `mod` 11 /= 0]
2 : 3 : 5 : 7 : 11 : sieve []    -- ここで例外が発生

先ほど ints 関数を見たので、sieve 関数がリストの要素をゾロゾロと生み出しているように見える。 ^^;

ところで、上記では有限のリストに適用したので、例外が発生してしまった。

Prelude> sieve ([2..11])
[2,3,5,7,11*** Exception: :1:4-54: Non-exhaustive patterns in funct
ion sieve

例外が発生しないようにするには take 関数を使うか、

Prelude> take 5 $ sieve [2..]
[2,3,5,7,11]

空のリストに適用したときに空のリストを返すようにしておく。

sieve [] = []

 

Python で実装

さて、最初に Python におけるエラトステネスのふるいの実装例を挙げた。そこでは再帰的な定義をしたが、アルゴリズムとしては素朴な内容となっている。「素朴」である理由は、エラトステネスの篩 – Wikipedia のアルゴリズムの説明の中で、「ステップ 4」の一部の判定を省略しているため。

探索リストの最大値が素数リストの最大値の平方よりも小さい場合、素数リストおよび探索リストに残っている数が素数となる。

この判定をする理由については、「エラトステネスの篩い」の説明、「エラトステネスの篩いの原理」の「2.どの数の倍数を取り除くか」がわかりやすい。しかし、ここではこの判定を省略し、「素朴」に倍数を消す作業をリストの終りまで続けることにした。

 

ループを使って

はじめは、ごく普通に ループを使って実装してみる。

def sieveByHead(L):
    """ 先頭の要素で割り切れない要素のリストを返す """
    result = []
    for e in L[1:]:
        if e % L[0] != 0 :
            result.append(e)
    return result

def sieve(L):
    """ 渡されたリストの中の素数を返す """
    result = [L[0]]
    while L:
        sieved = sieveByHead(L)
        if sieved: result.append(sieved[0])
        L = sieved
    return result

print sieve(range(2,100)) 

 

上記の sieveByHead 関数をリスト内包表記で置きかえるとコンパクトになる。

def sieve(L):
    """ 渡されたリストの中の素数を返す """
    result = [L[0]]
    while L:
        sieved = [x for x in L if x % L[0] != 0]
        if sieved: result.append(sieved[0])
        L = sieved
    return result

 

ジェネレータを使って

最初に挙げた再帰的な表現だと、有限のリストにしか適用することができない。 Haskell のように無限リストを扱うには、ジェネレータで書き直す必要がある。

再掲すると、

def sieve(L):
    if not L: return []
    else:
        return [L[0]] + sieve([x for x in L[1:] if  x % L[0] != 0])

これをジェネレータに変更すると、

def sieve(L):
    head = L.next()
    yield head
    for e in sieve(x for x in L if x % head != 0):
        yield e

ジェネレータは next() の呼出しによって、対象となる要素が一つ先へ進む。ジェネレータを代入する変数 L をリストと見たてるなら、最初の next() の呼出しによって先頭の要素がなくなったとイメージするとわかりやすい。また、リスト内包表記を ジェネレータ式 で置き換えていることにも注意。

上記の関数を使うには、無限リストを生成する関数を定義して、その呼出し結果を sieve 関数に渡す。

def ints(n):
    """ n 以降の自然数の無限リスト """
    while True:
        yield n
        n += 1

primes = sieve(ints(2))
for i in range(10):
    print primes.next()

または、有限のリストであるなら、iter 関数を適用してから呼出す。

print list(sieve(iter(range(2,100))))

 

しかし…

ここまで来ても、再帰的な定義を見ると、やはりどこかイメージしきれないという感じがする。 ^^; 答えが得られた後もスッキリとしない。なぜだろう。 (+_+) 単に慣れていないのか、そもそも根本的にどこか理解の仕方が違うのだろうか? わかっている人にとってみれば、「何を当り前のことを…」と思うのだろうけれど、その「当り前の考え方」が自分には感覚的に納得しきれないのでこだわってしまう。また、ツールとして再帰を利用することができない。

パタッ(o_ _)o~†

もう一度別の角度からアプローチしていこう…。

Python のジェネレータ (5) - 再帰とジェネレータと Composite

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

1. 例

例えば、「リストの要素を 2 倍したい」とする。

リスト内包表記や map 関数を使うなら、以下のように書ける。

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

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

 

2. for ループ

単純に、for ループでリストを走査しながら、要素を2 倍するなら、

def double(L):
    result = []
    for e in L:
        result.append(e*2)
    return result

print double(L)

これをジェネレータで置き換える。

def gDouble(L):
    for e in L:
        yield e * 2

for e in gDouble(L):
    print e

 

3. 再帰

再帰で書くと、

def rDouble(L):
    if not L: return []
    else:
        return [L[0]*2] + rDouble(L[1:])

print rDouble(L)

これをジェネレータで置き換えてみる。ジェネレータは要素をいっぺんに返さず、処理した要素ごとに返すようにする。

def grDouble(L):
    if not L: return
    else:
        yield L[0]*2
        for g in grDouble(L[1:]):
            yield g

for e in grDouble(L):
    print e

ジェネレータを再帰的に呼出すには、再帰的に適用したい部分を for に投入し (next() が呼出されるため)、その要素を yield で返す。渡された引数 L の要素がなければ、何もせず return でジェネレータを終了する。

次のように書き直すこともできる。

def grDouble(L):
    if L:
        yield L[0]*2
        for g in grDouble(L[1:]):
            yield g

 

4. Composite

ジェネレータを再帰的に呼出すことに、何かメリットがあるのだろうか?

ツリー状のノードの要素を走査するためにジェネレータが使われている例 を目にした。これを見ると、Iterator パターン Visitor パターン を連想する。

試しに、Composite パターン のオブジェクトに対して、その要素を走査する関数をジェネレータで定義してみる。

class Component:
    pass

class Composite(Component):
    def __init__(self):
        self.components = []
    def add(self, component):
        self.components.append(component)
        return self
    def __iter__(self):
        return iter(self.components)

class Leaf(Component):
    def __init__(self, val):
        self.val = val

def gComposite(composite):
    if isinstance(composite, Leaf):
        yield composite.val
    else:
        for c in composite:
            for g in gComposite(c):
                yield g

c = Composite().add(
        Leaf(100)).add(
        Leaf(200)).add(
        Composite().add(
            Leaf(1000)).add(
            Composite().add(
                Leaf(10000)).add(
            Leaf(2000))).add(
        Leaf(300)))

for e in gComposite(c):
    print e

Composite クラスの __iter__() メソッドの定義については、Python のイテレータ (3) を参照。

2008年10月19日日曜日

wxPython の準備 – Python で GUI

1. 標準添付の Tkinter は使わない

Python で GUI を作成したい。

wxPython でお手軽 gui によると、

Python には Tkinter, PyQt, PyGTK, wxPython などの gui toolkit があります。 いろいろと物色した結果次のことが分かりました。

  1. これらの toolkit のマニュアル、チュートリアルは極めて不備である。
  2. wxPython はデモスクリプトがあるだけまだましである。
  3. wxPython はいろいろな widget が揃っている。
  4. wxPython は最近人気があるようである。(その結果として web resources が他の toolkit より多い。)

Python に標準で Tkinter がついている。

16. Tkを用いたグラフィカルユーザインターフェイス16.1.2.2 簡単なHello Worldプログラム を実行すると、シンプルなウィンドウが表示される。

ただし、Tkinter  は、動作が遅く、長い間アップデートされていない。

Introduction to wxPython

The offcial python "toolkit" is TkInter. It is slow, looks terrible on all platforms, it has not been updated for ages. … It remains a mystery, why it was not excluded years ago.

よって、Tkinter 以外のものを使う。

 

2. wxPython と wxWidgets の関係

wxPython 1: はじめに によると、

wxPythonとは、Pythonで GUIアプリケーションを作るためのツールキットです。…

wxPythonは wxWidgetsという一般的なツールキットが元になっていて、…

wxWidgets – Wikipedia

wxWidgetsとはクロスプラットホームなウィジェット・ツールキットであり、C++で記述されているが、多くのプログラミング言語向けにバインディングが用意されており、PythonPerlJavaScriptなどから使うことが出来る。

wxWidgets のベースは C++ で、Python から使うことができる。

wxWidgets のダウンロードページ によると、

Not using C++? Get wxWidgets from the wxPython, wxPerl, and wx.NET download sites. Other ports...

 

3. wxPython のダウンロードとインストール

wxPython のダウンロードページ に、Python のバージョンと対応した wxPython が用意されている。

現在、最新の Python のバージョンは 2.6。ただし、Google App Engine を使いたいので、Python 2.5 に対応したものを利用する。 Pure Python によると、

The Python runtime environment uses Python 2.5.

wxPython Download より、Python 2.5 の win32-unicode をダウンロードしてインストール。デフォルトでは

  • C:\Python25\Lib\site-packages

にインストールされた。

IDE にPyScripter を使っている場合、インストール後、一度 PyScripter を起動しなおす必要がある。

 

4. wxPython の入門サイト

特に、The wxPython tutorialIntroduction to wxPython における

  • 「wxPython」の構造
  • wxPython API

は、全体を把握するのにわかりやすい。ドキュメントを読むための手がかりとなる。

 

5. wxPython のドキュメント

本家のドキュメントは

この中で頻繁に見ることになりそうなのは、

最初は、以下から読む。

Python で部分適用 - functools モジュールの partial 関数

1. Python は一部の引数を与えて関数を呼び出すことができない

Haskell では、関数に複数の引数があるとき、先に一部の引数のみ渡しておき、後から残りの引数を渡すことができる。一部の引数を与えることを「部分適用」と言う。

Python では、普通そういうことはできない。例えば、3つの引数を足し合わせる関数 addThree 関数に対して、1つの引数だけ与える。

def addThree(a,b,c):
    return a + b + c

print addThree(1)

上記を実行すると、引数が足りないとエラーが表示される。

exceptions.TypeError: addThree() takes exactly 3 arguments (1 given)

 

2. functools の partial 関数で部分適用

6.6 functools モジュールの partial 関数を使うと、部分適用を利用できる。

partial(func[,*args][, **keywords])

Return a new partial object which when called will behave like func called with the positional arguments args and keyword arguments keywords.

例えば、先ほど定義した addThree 関数に対して、partial 関数を使い、引数を1つだけ与える。

import functools

addTwo = functools.partial(addThree,1)
print addTwo(2,3)

partial 関数により、addThree 関数の最初の引数だけ渡した関数 addTwo を作成した。その後、addTwo 関数に対して、残りの二つの引数を与えている。

以下では、部分適用をした結果から、更に部分適用した addOneFromTwo 関数を作成し、元の addThree に二つ引数を渡した関数 addOne を定義した。

addOneFromTwo = functools.partial(addTwo,2)
print addOneFromTwo(3)

addOne = functools.partial(addThree,1,2)
print addOne(3)

部分適用により、予め抽象的な関数を定義しておき、部分適用によって具体的な関数を導くことができる。上手く使えば、関数のモジュール化 を促進できる。

 

キーワード引数を利用して

キーワード引数を使うと、部分適用するときの引数を特定できる。

addTwo_ = functools.partial(addThree,c=3)
print addTwo_(1,2)

addOne_ = functools.partial(addTwo_,a=1)
print addOne_(b=2)

addOneFromTwo = functools.partial(addThree,a=1,c=3)
print addOneFromTwo(b=2)

 

3. reduce 関数に対して部分適用し、具体的な関数を導く

リストの要素を足し合わせる sum 関数は、畳み込み関数 reduce より導くことができる。

なぜ関数プログラミングは重要か によると、

sum = reduce add 0

reduce 関数に対して、部分適用することによって sum を定義している。

Python の reduce 関数の使い方は、

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

reduce 関数に対して部分適用を利用し、シーケンスを後で与えるようにして、sum 関数を定義してみる。

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

import operator
from functools import partial

# 合計
mysum = partial(reduce, operator.add)
print mysum(L)

(functools.partial と書くのが面倒だったので、import の仕方を変更した。)

上記の書き方から、sum 関数が reduce というメタ的な関数の特殊パターンであるという雰囲気が伝わって来る。

Python では、(+) のように、二項演算子を渡すことができないので、3.10 operator モジュールの add を利用した。

 

4. 関数を合成する関数を定義

Haskell の 関数を合成する(.) 関数を、partial を使い Python  で定義してみる。

関数を合成する関数名を `c’ とした。

c = lambda f,g: partial(lambda f,g,h: f(g(h)), f, g)

def … return で定義した方が読みやすいかな。

def c(f,g):
    return partial(lambda f,g,h: f(g(h)), f, g)

それとも、関数をネストして定義してみる。

def c(f,g):
    def _(f,g,h):
        return f(g(h))
    return partial(_,f,g)

c 関数を使ってみる。

add3 = lambda x: x+3
mul3 = lambda x: x*3

print c(add3,mul3)(10)   # 33
print c(mul3,add3)(10)   # 39

 

関連記事

Firefox でウェブサイトの表の内容をコピーして表計算ソフトに貼り付ける

Firefox のアドオン「Table2Clipboard」をインストール。

 

方法

例えば、気象庁 | 毎日の全国データ一覧表 日別値 のデータの一部を得たいとき、Ctrl キーを押しながらドラッグして範囲を選択。

Ctrl + Shift + C でコピー。

081019-001

 

表計算ソフトにペースト。

081019-002

 

OpenOffice 3.0, Google スプレッドシート では、表の内容をコピーするときに Ctrl + C でも上手くペーストすることができた。ただし、Excel 2003 では必ず Ctrl + Shift + C でないとダメ。

 

関連記事

2008年10月18日土曜日

Python で二進数、十進数の変換

1. 二進数から十進数へ

Python で、数値を 2 進数から十進数に変換したい。

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

int([x[, radix]])

文字列または数値を通常の整数に変換します。…
radix 引数は変換の基数を表し、範囲 [2, 36] の整数またはゼロをとることができます。

long([x[, radix]])

文字列または数値を長整数値に変換します。…
radix 引数は int() と同じように解釈され、x が文字列の時だけ与えることができます。

int() 関数を、このような目的で使えることを知らなかった。(@_@;)

例えば、2進数を表す文字列 1010 を、十進数に変更する。

>>> print int('1010',2)
10

 

2. 十進数から二進数へ

十進数から2進数へ変換するには format 関数を使う。

PEP 3101: Advanced String Formatting によると、

In Python 3.0, the % operator is supplemented by a more powerful string formatting method, format(). Support for the str.format() method has been backported to Python 2.6.…

There’s also a format() built-in that will format a single value. It calls the type’s __format__() method with the provided specifier:…

'b' - Binary. Outputs the number in base 2.

Python 2.6 であれば、次のように書ける。例えば、十進数の 10 を、2進数に変換する。

>>> print format (10,'b')
1010

 

GMPY

Binary numbers には

を使った方法が紹介されていた。

 

3. その他

十進法 – Wikipedia によると、

十進記数法は単に十進法と呼ばれることもあるが、これを「10 進法」と書くのは好ましくない。10 が十を意味するのは十進記数法だからであり、例えば二進記数法ならば二を意味する。漢数字にはそのような曖昧さはない。英語でも同様に base 10 より decimal が好まれる。

 

参考サイト

関連記事

2008年10月17日金曜日

Python のデコレータ式 (3) - デコレートする側とデコレートされる側に引数がある場合

Python のデコレータ式 (2) のつづき

1. デコレートされる関数に引数がある場合を考える

これまで試したデコレータ式は、デコレートされる側の関数が引数を取らなかった。

Python のデコレータ式 (1) より、

def D(f):
    print u"デコレータが実行された"
    return f

# デコレータを適用したときに、関数 D が適用される。
@D
def hoge():
    print "hoge"

hoge()

前回は `デコレートする側に引数がある’ 場合について考えた。

Python のデコレータ式 (2) より、

def D(arg):
    def _(f):
        def __():
            print "*--" * arg
            f()
            print "--*" * arg
        return __
    return _

@D(5)
def hoge():
    print "hoge"

hoge()

今回は、デコレートされる関数に引数がある場合を考える。

 

2. デコレータが受け取る関数の引数を固定しないために、可変引数を使う

デコレートする関数は、デコレートされる関数の引数について予め分からない。どのような型の値が渡されるか未知であり、関数が引数をいくつとるのか知らない。

もし、デコレータの受け取る関数の引数の数が固定されていたら、デコレータは使いにくい。これに対処するには、可変引数を使う。

デコレータについて考える前に、Python の可変引数 の書き方を復習しておく。

def test(*args):
    for e in args:
        print e

test(1,2,3,"hoge","piyo")


def test2(**keywords):
    for k,v in keywords.iteritems():
        print k, v

test2(a=100, b=200)

Python では、引数の前にアスタリスクを付けると、可変引数となる。

*args, **keywords

 

3. デコレートされる関数の引数が一つの場合

最初は、「デコレートされる側の関数の引数が一つ」の場合について考える。

def D(f):
    def _(arg):
        print "*--" * 10
        f(arg)
        print "--*" * 10
    return _

@D
def hoge(n):
    print "hoge" * n

##hoge = D(hoge)
hoge(3)

これを実行すると、

*--*--*--*--*--*--*--*--*--*--
hogehogehoge
--*--*--*--*--*--*--*--*—*—*

コードが実行される様子を順に考える。上記コード中のコメントアウトしてある、デコレータ式を使わない場合で考えると分かりやすい。

  1. デコレータ関数 D に、関数 hoge を渡すと、`_ 関数’ が返される。このとき引数 f に hoge がセットされる。
  2. hoge(3) の呼出しは、`_(3)’ となり、ネストされた関数が実行される。

追記 (2009.12.22) : 上記の手順 1 で返される `_ 関数’ は、

引数を一つ受け取る関数

として定義されている。デコレートしたときに、何か特別な機能として arg に引数が渡される仕組みがあるわけではない。

 

4. デコレートされる関数の引数が複数の場合

次に、デコレートされる関数の引数が、複数の場合に対応できるように変更する。

def D(f):
    def _(*args):
        print "*--" * 10
        f(*args)
        print "--*" * 10
    return _

@D
def hoge(n):
    print "hoge" * n

@D
def piyo(a,b):
    print "piyo" * a
    print "piyo" * b

##hoge = D(hoge)
##piyo = D(piyo)
hoge(3)
piyo(3,5)

実行した結果は、

*--*--*--*--*--*--*--*--*--*--
hogehogehoge
--*--*--*--*--*--*--*--*--*--*
*--*--*--*--*--*--*--*--*--*--
piyopiyopiyo
piyopiyopiyopiyopiyo
--*--*--*--*--*--*--*--*--*--*

デコレートする関数におけるネストした関数の引数を

*args

とすることにより、引数の数が異なる hoge(n), piyo(a,b) 関数をデコレートできるようになった。

`_ 関数’ は、

複数の引数を受け取る関数

として定義されている。

 

5. デコレートされる関数の引数にキーワード引数が含まれる場合

デコレートされる関数の引数に、キーワード引数 が含まれている場合は、次のように記述する。

def D(f):
    def _(*args, **keywords):
        print "*--" * 10
        f(*args, **keywords)
        print "--*" * 10
    return _

@D
def hoge(n):
    print "hoge" * n
@D
def piyo(x,y):
    print "piyo" * x
    print "piyo" * y
@D
def fuga(a,b,c=8,d=10):
    print "fuga" * a
    print "fuga" * b
    print "fuga" * c
    print "fuga" * d

hoge(5)
piyo(5,10)
fuga(1,2)
fuga(1,2,3)
fuga(1,2,c=10)
fuga(1,2,c=10,d=5)

これまでと同じように、デコレートされたときに

複数の引数とキーワード引数を受け取る関数

が返されるように定義する。

 

6. デコレートする関数、デコレートされる関数に引数がある場合

前回は、デコレートする関数に引数がある場合について考えた。

デコレートする関数と、デコレートされる関数に引数がある場合を定義してみる。デコレータに引数が渡されるので、ネストが一つ増えることに注意する。

def D(n):
    def _(f):
        def __(*args, **keywords):
            print "*--" * n
            f(*args, **keywords)
            print "--*" * n
        return __
    return _

@D(15)
def hoge(n):
    print "hoge" * n

@D(10)
def fuga(a,b,c=8,d=10):
    print "fuga" * a
    print "fuga" * b
    print "fuga" * c
    print "fuga" * d

##hoge = D(15)(hoge)
hoge(10)
fuga(1,2,c=5,d=7)

実行した結果は、

*--*--*--*--*--*--*--*--*--*--*--*--*--*--*--
hogehogehogehogehogehogehogehogehogehoge
--*--*--*--*--*--*--*--*--*--*--*--*--*--*--*
*--*--*--*--*--*--*--*--*--*--
fuga
fugafuga
fugafugafugafugafuga
fugafugafugafugafugafugafuga
--*--*--*--*--*--*--*--*--*—*

 

7. デコレータを重ねて利用する

最後に、

  1. デコレートに必要な情報をデコレータの呼出しのときに渡し、
  2. デコレータを重ねて利用する

と次のようになる。

def D(top, bottom, x, y):
    def _(f):
        def __(*args, **keywords):
            print top * x
            f(*args, **keywords)
            print bottom * y
        return __
    return _

@D("-#-", "-#-", 4, 10)
@D("--*", "*--", 3, 9)
@D("-+-", "+-+", 2, 7)
def fuga(a,b,c=8,d=10):
    print "fuga" * a
    print "fuga" * b
    print "fuga" * c
    print "fuga" * d

fuga(1,2,c=3,d=4)

結果は、

-#--#--#--#-
--*--*--*
-+--+-
fuga
fugafuga
fugafugafuga
fugafugafugafuga
+-++-++-++-++-++-++-+
*--*--*--*--*--*--*--*--*--
-#--#--#--#--#--#--#—#—#—#-

これで、一つのデコレータで様々な装飾を施せるようなった。 ^^

下図に、色分けして見やすくしたコードを示す。

081016-001

Haskell でフィールドに型変数を含んだ型の print

前回の「Haskell で代数的データ型の値を print で出力」では、型のフィールドに型変数が含まれていなかった。今回はフィールドに型変数が含まれる場合について試してみる。

 

Iremono a 型

例えば、何でも入る「入れ物」を想定し、型変数を一つフィールドに持つ `Iremono a 型’ を定義。

data Iremono a = Iremono a

これを print で出力できるようにしたい。

 

instance Show Iremono

ここで、次のように Iremono を Show クラスのインスタンスにすると、

instance Show Iremono

次のように怒られる。 (+_+)

    `Iremono' is not applied to enough type arguments
    Expected kind `*', but `Iremono' has kind `* -> *'
    In the instance declaration for `Show Iremono'

これは kind と言って、型を分類するための概念らしい。 (cf. Kind – HaskellWiki) 詳しくはわからないので横に置いておく。 ^^; QQQ

 

instance Show (Iremono a)

ところで、Show クラスのインスタンスにしたい対象は Iremono 型ではなくて Iremono a 型。( Iremono は型ではなくて、型コンストラクタ) だから次のように、

instance Show (Iremono a) where
    show (Iremono x) = "nakami ari"

main = print $ Iremono 100

上記は入れ物の中に中身がある場合に「中身あり」と表示させるようにしただけ。

 

instance Show a => (Iremono a)

しかし、普通 print と言ったら、その中身を表示させたいので、

instance Show (Iremono a) where
    show (Iremono x) = show x

これでいいのかなと思いきや、また怒られた。 (+_+)

    Could not deduce (Show a) from the context (Show (Iremono a))
      arising from a use of `show' at functest.hs:4:23-28
    Possible fix:
      add (Show a) to the context of the type signature for `show'
    In the expression: show x
    In the definition of `show': show (Iremono x) = show x
    In the definition for method `show'

deduce とか難しいことが書かれている (@_@;) けれど、ゆっくりとエラーの内容を読むと、show x の式が問題なことがわかる。show x の x と言ったら、「入れ物」の `中身’ のこと。これはどんな型でも入れることができるので、show を適用できるかどうかはわからない。だから、次のように `中身’ も Show クラスのインスタンスでないといけないことを示す必要がある。

instance Show a => Show (Iremono a) where
    show (Iremono x) = show x

 

全体を示すと、

data Iremono a = Iremono a

instance Show a => Show (Iremono a) where
    show (Iremono x) = show x

main = print $ Iremono 100

結果は、

100

 

deriving

前回教えていただいたように deriving を使うと、instance Show … を書かなくても、

data Iremono a = Iremono a deriving Show

main = print $ Iremono 100

結果は、次のように表示される。

Iremono 100

 

参考

Firefox のスクロールが遅い – ハードウェア アクセラレータの値を変更

1. 画像がたくさんあるページで、異常に重くなる

Firefox が特定のページで重い。

画像がたくさんあるとダメなのだろうか? 例えば、

なんてスクロールさせると CPU が異常に反応して、もたつく。

しかも、不思議なことに、マウスを画像の上に持っていくと、マウスの動きが激烈に悪くなる。 (+_+)

スクロールが遅い原因として、表示の拡大縮小が原因 だったことがあった。しかし、今回の原因はこれではない。

 

2. background-attachment を設定しても効果なし

調べてみると、firefoxだとこのページが重いです。 - 教えて!goo

* {
background-attachment:scroll !important;
}


これをuser.jsではなく、userContent.cssに書けばいいです。

アドオンとして Stylish を入れている。上記を userContent.css に書かなくても、Removes background-attachment: fixed | userstyles.org において、「Load into Stylish」のボタンを押して簡単にインストールできる。

しかし、効果はなかった。

に、重くて、もたつくページの例として、

があった。開いたら、なかなかスクロールさせることができない。

 

3. ハードウェア アクセラレータ の値を変更する

更に調べていたら、非常に気になるスレッドが。↓ (@_@;)

自分の PC のマザーボードは、GA-MA78GM-S2H (rev.1.1) なので、まさに上記に該当する。(cf. はじめての自作 PC の検討 2)

AMD の 780G で HD3200 。かつ Windows XP でブラウザのスクロールの問題。ATI 絡みの問題であることが書かれている。

とりあえずの対策として、次のようなことが書かれていた。

  • - right-click on your desktop
  • - click "properties"
  • - click the "settings" tab
  • - click the "advanced" button
  • - click the "troubleshoot" tab
  • - move the "hardware acceleration" slider ONE point to the left, so the description says something like "Disable cursor and bitmap accelerations. Use this setting..."

(同上より)

081016-001早速、上記の設定を試してみた。

  • コントロールパネル > 画面 より、設定タブ > 詳細設定ボタン

を押す。トラブルシューティングタブ を開いて、

  • ハードウェア アクセラレータ

の横スクロールバーが「最大」になっていたので、これを一つ左へ移動。

これにより、

「カーソルおよびビットマップのアクセラレータを無効」

となる。なるほど、それでカーソルの動きが悪くなっていたのか。

 

4. 結果、スムーズに動くようになった

スクロールが普通にスイスイと動くようになった。 ^^  マウスジェスチャの動きもスムーズに。

これで、Yet Another Smooth Scrolling をインストールしても問題がなくなった。

しかし、最近、カクカクとした動きに慣れていたので、滑らかなスクロールにしたら、目が回った。(@_@;)

 

5. グラフィックドライバの問題

追記 (2009.1.12) : ATI Catalyst 8.12 にアップデートしたら画像の表示がスムーズになった。