カテゴリー: Haskell

Home / カテゴリー: Haskell

Haskellのクラスはちがう

2013-03-13 | Haskell | コメントはまだありません

本の何処かに「Haskellのクラスは、JavaやPythonのクラスとは違う概念だから!Pythonとかのクラスのことはきれいに忘れてほしいな!」とか書いてある。
違う概念なんだったら違う言葉で表現していてほしい。Haskellのver1.0が世に出たのは1990年だというから、そのころならまだ、クラスを別の呼び方にするには間に合ったんじゃないのか。なんなのか。
いまだに用語のアレコレが染み付いておらず、電車の中で読むたびに寝ています。
という愚痴。

いつか学習しているうちにたどり着いて見つけるのかもしれないが、とあるオブジェクトのかたちと振舞いを定義した塊としてのクラス、Pythonやphpで言うところのクラスのような定義の仕方についての記述がなかなか出てこないことが、不安として横たわっています。
既知の「クラス」をモジュールとかのあたりのファイルを単位とした何かで担うのであろうか。
いちいち置き換えて考えようとするのは悪手なんだろうとは思うんだけど、漠然とわからないまま彷徨うあまり、手がかりを探してしまう感じ。

データ型を定義するの辺り

2013-03-13 | Haskell | コメントはまだありません

すごkellp112の「形づくる」のあたり。
#ページ進んだとおもいきや戻ってという読み方をしているので一向に進みません。

テキスト読みながらぼんやり書いてぼんやり動かして、数日うまくいかないことがありました。
さっき解決したのでメモ。

円と矩形の定義をして、面積を得る関数を書きましょうみたいなあたり。

data Shape = Circle Float Float Float |
             Square Float Float Float Float 
     deriving (Show, Read)

area (Circle _ _ r  ) = pi * r ^ 2
area (Square _ _ w h) = w * h

piは予約語なんだな。
こう書いて、

*Main> area $ Circle 10 10 10
314.15927

うん。

*Main> area $ Square 10 10 10 10
*** Exception: shape.hs:5:1-39: Non-exhaustive patterns in function area

えー。
dataの宣言のところの改行が良くないのだろうかとか、インデントの位置だろうかとか、そらもういろいろ試したがここから数日間動けず、イライラしておりました。
Circleの面積は得られる。
Squareは得られない。Squareが予約語なのかも!とかで書き換えてみてもダメで、ああそうだ、なんか型の宣言を書いといたほうがいいみたいな話だったなあと思い、

data Shape = Circle Float Float Float |
             Square Float Float Float Float 
     deriving (Show, Read)

area :: Shape -> Float
area (Circle _ _ r  ) = pi * r ^ 2
area (Square _ _ w h) = w * h

1行書き足し。
これがじつは正解で、

*Main> area $ Square 10 10 10 10
100.0

うごいた。
本には「型宣言を書いておくことはよい習慣です」くらいの書き方してたけど、違うじゃん。
必須じゃん。

続・身についてないっぽいポイント

2013-03-06 | Haskell | コメントはまだありません

先日の記事で、

(\x y -> x + y)

これが何してんのかすぐに読めない。
どうも関数\xと引数yって読んでしまい、思考が脱輪するのね。

Prelude> :t ($)
($) :: (a -> b) -> a -> b

こういうのを、(a->b)は関数で、引数がaで、bがreturnされる、と読めない。
a -> (a -> b) -> bって書いてくれればいいのに。

と書いた。

a -> (a -> b) -> bって書いてくれればいいのに。

これ中置き関数じゃないか。a `mod` bじゃないか。
で、a `mod` bはもともとは(mod) a bなんだから、結局

($) :: (a -> b) -> a -> b

の形に戻ってくるんだわな。
自分の視覚にとっての読みやすさの問題なんだなー純粋に。

中国の剰余定理

2013-03-05 | Haskell | コメントはまだありません

逆ポーランド記法の電卓があるらしいと知り、hpの10,000円くらいの高機能電卓、それもなぜかグラフ表示までできるようなやつをみてびっくり、amazonのレビューも筋金入りの数学オジサマがたの巣窟となっている感があり、いやはや世の中にはいろんな世界があるものだと逃げ帰ってきた次第です。
で、hp 50g ハイエンド グラフ電卓という電卓のレビューで、

しかしながら2300を超えるといわれる関数には、整数a, b, cを与えてau + bv = cを満たす整数u, vを計算するIACUVという関数や、中国の剰余定理の解を求めるICHINREMやCHINREMなど数学の専門家でもなければなかなかお目にかかることのないものまで含まれています。

この電卓にはいっぱい関数が入ってるらしい。
そ、そうですか。
言われてみれば普通の√とかもあれ、関数か。どころか、加減乗除ぜんぶ関数だ。それのもっといろんなことができるのが関数電卓だということなんだな。
あたしがこの電卓を買っても全然アレなので買うつもりはないけど、
中国の余剰定理
という一文に目が止まりました。なにこれ。なんだかマイナーな関数の搭載がされているという話ですが、そういう定理があるのね。
んでWikipediaの「中国の剰余定理」を見てみる事にしました。

『孫子算経』には、「3で割ると2余り、5で割ると3余り、7で割ると2余る数は何か」という問題とその解法が書かれている。中国の剰余定理は、この問題を他の整数についても適用できるように一般化したものである。

あら、シンプルな話。これならあたしにも何とかできそう。
#孫子算経というのは中国の古い書物で、数学や暗号について書かれたものだそうです。

解いてみる

三種類の数で割った剰余がそれぞれtrueだったらナンチャラ、というのを書けばいいんだろう。
[1..]とかで順番に洗っていけばいいんだわな。
[1..]だと終わりがないからいかんな。さしあたり[1..100]とかにしとこう。見つからなかったら1000とか10000とか数字を上げていけばよろしい。
#ちなみに、うっかり見ちゃったので、上の条件を満たすのは最小で23だということまでは把握しています。ちぇ。

こんなかんじ?

Prelude> let foo x = (x `mod` 3 == 2, x `mod` 5 == 3,x `mod` 7 ==2 )

さっそくためします。

Prelude> foo 23
(True,True,True)
Prelude> foo 22
(False,False,False)
Prelude> foo 8
(True,True,False)

うん。んー、うん。

Prelude> :t foo
foo :: Integral a => a -> (Bool, Bool, Bool)

タプルで返ってくるのは不便だ。ぜんぶ満たしてたらTrueでいいんだけど。

Prelude> let foo x = (x `mod` 3 == 2 && x `mod` 5 == 3 && x `mod` 7 ==2 )
Prelude> :t foo
foo :: Integral a => a -> Bool

どうかな。

Prelude> foo 23
True
Prelude> foo 22
False
Prelude> foo 8
False

書き方あってたみたい。で、リストの中から条件に合うものを拾うのはfilterだということなので、

Prelude> :t filter
filter :: (a -> Bool) -> [a] -> [a]

Boolが返る関数とリストを渡すのね。

Prelude> filter foo [1..100]
[23]

お。

Prelude> filter foo [1..1000]
[23,128,233,338,443,548,653,758,863,968]

おお。
実に大したことはないんですが、嬉しい。
少なくともいまのうちは、htmlだcgiだバッチだと吠えるよりは、こういう算数あそびに使ってるほうがいいのかも。

そーいえば

takeで先頭n個の取得ができるんだった。

Prelude> take 1 filter foo [1..1000]

:1:1:
    The function `take' is applied to four arguments,
    but its type `Int -> [a0] -> [a0]' has only two
    In the expression: take 1 filter foo [1 .. 1000]
    In an equation for `it': it = take 1 filter foo [1 .. 1000]

あちゃー。
そうか、このままだと take 1 で拾おうとするリストが”filter”になっちゃうんだ。
$つけよう。

Prelude> take 1 $ filter foo [1..1000]
[23]
Prelude> take 1 $ filter foo [1..]
[23]

これなら[1..1000]じゃなくて、[1..]でいいんだな。Lazyはよい。
少し賢くなった気分。

リストのスライス

2013-03-04 | Haskell | コメントはまだありません

Prelude> [1..5]
[1,2,3,4,5]

こんなリストさんで試す。
先頭n個を取りたいときはtakeを使うとあった。

Prelude> take 3 [1..5]
[1,2,3]

うん。わかる。

Prelude> 1:2:3:4:5:[]
[1,2,3,4,5]

これもおなじことなので、

Prelude> take 3 1:2:3:4:5:[]

:1:16:
    No instance for (Num [a0])
      arising from the literal `5'
    Possible fix: add an instance declaration for (Num [a0])
    In the first argument of `(:)', namely `5'
    In the second argument of `(:)', namely `5 : []'
    In the second argument of `(:)', namely `4 : 5 : []'

あるぇー?おなじじゃない模様。なんなの?
もしかして、と思い、遅延評価?だかをするみたいな風情の$を使ってみる。

Prelude> take 3 $ 1:2:3:4:5:[]
[1,2,3]

はーなるほどー。
で、

Prelude> take 3 [1..5]
[1,2,3]

先ほどこれはできたわけだがこっちはどうかなと試すと、

Prelude> take 3 $ [1..5]
[1,2,3]

同じなんだ。$は「右端まであるカッコ」だという記述があったので、つまりそれは、

Prelude> take 3 ([1..5])
[1,2,3]

はーなるほど。
さっきのエラー文言そのものをみてみる。おかしいと言っているのは16桁目、「5」のあたり。
「リテラル「5」から発生するための(Num[a0])インスタンスはありません。」
エキサイト様すいません、わかんないっす。

Possible fix: add an instance declaration for (Num [a0])

こっちは修正すべき箇所の指示かな。
「実例宣言を加える、のために(Num[a0])」
インスタンスの宣言を加えろ?Num [a0]ってのがよくわからない。
リストを処理しようとしたけど、数値リテラルだったからやーめた、という解釈をしてみます。

take 3する時点では、1:2:3:4:5:[]は1から5のリテラルと空リストの連なった何かであってそれはリストじゃないんだよと言われているのでしょうか。
で、

(1:2:3:4:5:[])

と書いたことで

[1,2,3,4,5]

に置き換わって、先頭3つをスライスできるようになりました、めでたし的なかんじ?

Prelude> 1:2:3:4:5:[] == [1..5]
True

うーん、よくわからない。わからんが、take 3さんに1:2:3:4:5:[]を渡しても駄目であるということは分かった。
それを分かったというのかというと、ちょっと微妙な気はしますが。