カテゴリー: Haskell

Home / カテゴリー: Haskell

Just!!

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

あいかわらず、すごいHaskellを読んで寝ています。
家に帰るとヨメとゲームやっちゃうし仕事中にHaskell遊びするわけにもいかんしで、本読んでばかりで手を動かしていません。だから実感なくて寝るんじゃないかバカなの。

いまだにHaskellの構文を見てピンとくるシーンが少ないです。あたしの脳がHaskellの文をパターンマッチングできてないんだわ。x:xsを認識するのがあたしの限界か?

今日はJustで遊んでみます。
Maybe a型という、なんかもういきなり「ハァ?」と思わずにいられないネーミングのそれですが、

*Main> :t Just 1
Just 1 :: Num a => Maybe a

*Main> :t Just 'A'
Just 'A' :: Maybe Char

*Main> :t Just "Abc"
Just "Abc" :: Maybe [Char]

*Main> :t Just []
Just [] :: Maybe [a]

*Main> :t Just [1]
Just [1] :: Num t => Maybe [t]

いたずらを思いついた。

*Main> :t Just Nothing
Just Nothing :: Maybe (Maybe a)

ほー。
こんだけかと言われそうですが、何かと勝手に制約を課しがちな、固いあたしの脳みそにとってはわりと快挙というか金星というか、ほめてあげたい感じ。えらいぞあたしの脳。

*Main> :t Just Just 1
:1:1:
    The function `Just' is applied to two arguments,
    but its type `a0 -> Maybe a0' has only one
    In the expression: Just Just 1

ダメだろうなとは思ったんだけど。

*Main> :t Just $  Just 1
Just $  Just 1 :: Num a => Maybe (Maybe a)

こうですね。

わからんとこだらけだけど読み進めよう、と思って読み進めると、次々にわからんことになって後戻り。
学ぶと難しいが使えば簡単、といったことをtnomura9さんも仰っていたので、まあまずは黙って手を動かせよってことですよね。

リスト操作に親しむ

2013-03-21 | Haskell | 1件のコメント

たしかPythonを使い始めてその良さが染み込み始めた頃、「リスト(っぽいいろんな型)の多彩さ」と「操作のシンプルさ」が気に入ったんだった。コードのスッキリした見た目も。
それまでのあたしは、配列と言ったら配列一種類であって、タプルとか辞書とか、なんのこっちゃだった。そんなのphpは一個の「配列」でおなじことやるよ?なんで分けてるの?って。
最初は楔形文字かと思っていたPythonのリスト内包表記なんかがだんだん読めるようになってきて、次第にphpで核プログラムのあちこちで、
リスト内包表記ができたら楽なのに
ここで関数を渡せたら楽なのに(仕事で使うphpは最新とは限りませんね!)
なんて思ったものでありました。

そういった視角からの目で見ますと、Haskellのいいところもやはり、リスト操作のシンプルさにあるのではないかと思えてきます。
Lispを学ぼうと思っていた頃は気持ち悪い概念だったcarとcdrも、こっちだとheadとtailですか、良い感じに慣れてきました。たぶんカーとかクダーとか、なにもイメージ出来ない奇妙な音で表現しているセンスに引いたんだと思います。「cdddr」とかああいうの。引くでしょ。
あれから年月は経ち、x:xsと書かれるだけで怒りゲージが上昇を始めていた当時のあたしはいません。
慣れるんだろうけど!慣れるんだろうけど!って連呼しながら腹を立てていた当時のあたしが非常に滑稽ではありますが、そういうみっともない足跡もブログに記していくのであります。ほんとに慣れるとは思ってなかった。
#昔話みたいに言ってますけど数週間前の話です

連長圧縮のとこでspanという関数が出てきていました。

Prelude> :t span
span :: (a -> Bool) -> [a] -> ([a], [a])
Prelude> span (<3) [1..10]
([1,2],[3,4,5,6,7,8,9,10])

ちょうど反対の概念であるところのbreakというのもあると。

Prelude> break (<3) [1..10]
([],[1,2,3,4,5,6,7,8,9,10])
Prelude> break (>3) [1..10]
([1,2,3],[4,5,6,7,8,9,10])

filterって関数もあったよねえ。これ便利だなーと実感している関数の一つ。

Prelude> filter (<3) [1..10]
[1,2]

Prelude> filter (>3) [1..10]
[4,5,6,7,8,9,10]
関数名 関数 リスト

の形式にみんな沿っていて、読みやすい。たぶんこれは慣れのせいで、(*2)を関数と認識できるようになった、というのが大きい。脳とは柔軟なものなんだな。

Prelude> span (=='A') $ init buff
("AA","BBCC")
Prelude> span (=='A') $ tail buff
("A","BBCCC")
Prelude> span (== head buff ) $ tail buff
("A","BBCCC")
Prelude> span (== head buff) $ tail buff
("A","BBCCC")
Prelude> break (== head buff) $ tail buff
("","ABBCCC")
Prelude> filter (== head buff) $ tail buff
"A"
Prelude> filter (/= head buff) $ tail buff
"BBCCC"

日々触るということが大事なのであります。

連長圧縮

2013-03-17 | Haskell | 1件のコメント

孤独のHaskellという記事があった。

遅刻して現場に付いたらRLEしてみようという例題(“AABBCCC”を”A2B2C3″にする関数を書こう)をやっていて

画像フォーマットのgifとかこういう圧縮やってんだよね。仕組みはよく知らないけど、AABBCCC→A2B2C3くらいならやれそうじゃん?
甘かった。

連長圧縮はともかく文字列の分割を考える

文字列をx:y:xsとかで取り出してx==yのとき云々とか考えてたけどなかなかうまく行かず、あちこちをさまよっている間に、groupという関数の存在を知ります。

Prelude> import Data.List
Prelude Data.List> group "aabbccc"
["aa","bb","ccc"]

ほー。これは便利だ。あたしこういう関数の存在も知らないままトライしようとしてるわけか。無謀だなあ。
で、これの仕掛けはどうなってんの?
たしかHoogleというサイトが便利だったはず。

Data.List.group

group
Data.Listの中に収録されてる。
実装も見られるみたいなので恐る恐る見てみる。こええ。

group                   :: Eq a => [a] -> [[a]]
group                   =  groupBy (==)

短い!! groupByに(==)を与えた、えーと部分適用?の状態なんだな。
group “aabbccc”

groupBy (==) “aabbccc”
はおなじなんだということな?

*Main Data.List> group "aabbccc"
["aa","bb","ccc"]
*Main Data.List> groupBy (==) "aabbccc"
["aa","bb","ccc"]

ははーなるほど。

Data.List.groupBy

「special case of groupBy」とのことなので、次はgroupByを見てみる

groupBy                 :: (a -> a -> Bool) -> [a] -> [[a]]
groupBy _  []           =  []
groupBy eq (x:xs)       =  (x:ys) : groupBy eq zs
                           where (ys,zs) = span (eq x) xs

書いてあることはわからんながら、たった数行で表現しちゃうものなのね。根拠無く、もっと長大な何かをイメージしてた。

効果的な切り取り方をしてると感じたのは

groupBy eq (x:xs)       =  (x:ys) : groupBy eq zs
                           where (ys,zs) = span (eq x) xs

ここ。
spanの第一引数に1文字目と等しいか比較する関数(eq x)を食わせ、第二引数に1文字目いがいの文字列(ABBCCC)を食わす。
返り値はパターンマッチングでリストysとリストzsとして受け取る。スマートだなあ。

span

循環参照的泉鏡花的らせんに迷いこむような錯覚を覚え始めましたが、こんどはspanというのを見てみよう。その前に使ってみよう。

*Main Data.List> :t span
span :: (a -> Bool) -> [a] -> ([a], [a])

なんかこういう型宣言をすぐ取ってこれるのは便利なんだなと思えるようになってきた。
真偽値を返す関数とリストの何かを渡すと、タプルにふたつのリストを内包したブツを返す。
左様で。

*Main Data.List> span (==1) [1,2,3]
([1],[2,3])
*Main Data.List> span (==1) [5,1,2,3]
([],[5,1,2,3])
*Main Data.List> span (==2) [5,1,2,3]
([],[5,1,2,3])
*Main Data.List> span (==2) [1,2,3]
([],[1,2,3])
*Main Data.List> span (==2) [2,3]
([2],[3])
*Main Data.List> span (==2) [2,2,2,3]
([2,2,2],[3])

狙った値で切り取れるんだコレ。便利だなー!
“AABBCCC”の”A”で”ABBCCC”をspanするわけだな!

*Main Data.List> span (=='A') "ABBCCC"
("A","BBCCC")

はいはいはいはい。

#以下長くなったので畳みます

(さらに…)

Haskellとは

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

そもそものきっかけが「なんとなくカッコいい気がする」程度で歩きだしているのでどうでもいいことというか、何に役立てるつもりで勉強しているかという意識が希薄なんですが、そもそもHaskellって何なのかが気になったので、いつもどおりWikipediaさんに聞きました

Haskell は高階関数や静的多相型付け、定義可能な演算子、例外処理といった多くの言語で採用されている現代的な機能に加え、パターンマッチングやカリー化、リスト内包表記、ガードといった多くの特徴的な機能を持っている。また、遅延評価や再帰的な関数や代数的データ型もサポートしているほか、独自の概念として圏論のアイデアを利用し参照透過性を壊すことなく副作用のある操作(例えば 代入、入出力、配列など)を実現するモナドを含む。このような機能の組み合わせにより、手続き型プログラミング言語では記述が複雑になる関数でも Haskell ではたいていは実装が容易になる。

落語の演目にでも出てくるセリフか何かかねこれは。寿限無寿限無。
なにを指している文章なのかさっぱり、かけらも、理解できない。
「パターンマッチングやカリー化、リスト内包表記」というところだけ読めた。
圏論ってなんだろうということで調べてみると、さらにわけの分からない世界が広がっていた。なんだあれは。圏手とか射とか、何語だ。
こういう、途方もなく自分に縁遠い概念を勉強しようと思う時、どこから手を付けたらスタートできるんだろうか。
集合論とかがそうなのかなーと思って竹内なんとかさんの本も読んでるんだけど、やっぱり普通に使われてる言葉が既に意味不明で、
「あたしはこの本を読んでいいレベルに達していない」
ということをことごとく痛感させられているわけであります。

大人はたしかに当時、勉強しとけばよかったって言うよって言ってたが、ありゃホントだなあ。困ったなあ。

だれかが言ってたことですけど、Haskellを一言で言うと、Hello Worldを出力するまでに解説書の160ページを費やす言語であるというような一言を見かけました。
うん、他の言語だと一瞬で済むようなことに思うのに、遠いよね。
「純粋」な世界って大変なんだなあ。

andであるとかorであるとか

2013-03-15 | Haskell | 1件のコメント

すごいHaskellを読み続ける毎日。

身長と体重の数字を与えると、BMIをご丁寧に計算した上、お前は骨皮だのデブだのと罵倒する、いかにもHaskell(イメージ)なサンプルプログラムが掲載されていたので書いてみます。

bmiTell :: Double -> Double -> String
bmiTell weight height
        | bmi <= 18.5 = show bmi ++ " yase"
        | bmi <= 25.0 = show bmi ++ " hutu-"
        | bmi <= 30.0 = show bmi ++ " debu"
        | otherwise   = show bmi ++ " very debu"
        where bmi = weight / height ^ 2

返されるコメント部分は、ホントはもっと長い。長いコメント返すのが目的じゃないのでdebuとかにしました。
あたしは今のとこBMI23くらいなので(痩せました!)、あんま酷い罵倒はされません。よかったです。

で、この書き方がパターンマッチングと双璧をなす、かどうかはわからないが、関数の定義の際に便利な書き方であるということを覚えました。
switch文みたいだ。
個人的には、where以下で定義できる、変数やら関数やらの周辺に違和感を感じます。なんで後ろに並べるのか。手前の数行で使われる変数について知りたいときに、末尾を目で追わないといけないというのは不便な気がするんだけどなあ。
なんとなく、動作の内容が重要なのであって、値は二の次なんだよという姿勢の一環なのかなという感触。

文句はともかく、こういうのは繰り返し書き取りすることでしか身につかないので、手元で色々くだらないアレコレを書くことにします。

ohoho :: (Num a, Num a1) => a -> a1 -> [Char]
ohoho x y
      | x == 1 && y == 1 = "1 1"
      | x == 1 && y /= 1 = "1 ?"
      | x /= 1 && y == 1 = "? 1"
      | otherwise        = "Boo"

意味も中身もマトモにないけど書き取りの練習だからコレ。
で、ふと思う。
こういう、True and TrueはTrueでTrue or FalseはFalseですみたいなのはどう書いてるんだ実際。演算子あんのか。

*Main> 1 & 1
:1:3: Not in scope: `&'

*Main> 1 `&` 1
:1:4: parse error on input `&'

*Main> (&) 1 0
:1:1: Not in scope: `&'

ない。困りますそういうのは。
いやあるだろ。ないといろいろ無理だろう。探します。
「haskell 演算子」でぐぐりますと見つかったページが神がかっており、そこの一覧を眺めますと、ありました。
Haskellの演算子について纏めてみた@開発やプログラミングや家族や思考
すごい。

Bool

&じゃなくて&&だった。

Bool (&&) 論理積
Bool (||) 論理和

とあるので、さっそくやってみる。

*Main> (&&) 1 0
:1:8:
    No instance for (Num Bool)
      arising from the literal `0'
    Possible fix: add an instance declaration for (Num Bool)
    In the second argument of `(&&)', namely `0'
    In the expression: (&&) 1 0
    In an equation for `it': it = (&&) 1 0

おい。んー、表に「Bool」ってある。
あー。

*Main> (&&) True False
False

真偽値じゃないもん放り込んでんじゃねえよデブって言われてたんですね。デブじゃないし。BMI23だし。

Bits

当該ページの下の方に、似たようなのがあります。

Bits (.&.) ビットAND
Bits (.|.) ビットOR

びっと。ためしにやってみる。

Prelude> (.&.) 1 0
:1:1: Not in scope: `.&.'

Prelude> (.&.) True False
:1:1: Not in scope: `.&.'

そもそもスコープにないって。そんな関数知らねえよってこと?え?
あたしはそのへんピンときちゃう。

*Main> import Bits
*Main Bits>

今日は天気が悪い(あたしにとってはいい天気)なのでザジテン飲んでないんだ。頭冴えてます。

*Main Bits> (.&.) True False
:1:1:
    No instance for (Bits Bool)
      arising from a use of `.&.'
    Possible fix: add an instance declaration for (Bits Bool)
    In the expression: (.&.) True False
    In an equation for `it': it = (.&.) True False

*Main Bits> (.&.) 1 0
0

こっちは、真偽値じゃなくて01を受け取るのか。
冴えてもこの程度ですがあたしは生きています。

さらにピン

*Main> 1 `and` 1
:1:1:
    The operator `and' takes two arguments,
    but its type `[Bool] -> Bool' has only one
    In the expression: 1 `and` 1
    In an equation for `it': it = 1 `and` 1

だめか。だめとおもいきや、ちょっとエラーメッセージがちがう。
なんだ?

*Main> and True False
*Main> True and  False
*Main> 1 and 1
*Main> 1 and 0
*Main> 1 `and` 0
*Main> [1] `and` [0]
*Main> [True] `and` [False]

などと盲滅法に打ちまくり怒られまくったあとで、ようやっと
「but its type `[Bool] -> Bool' has only one」
とあるのに気づきます。なんでリストなのん……?

*Main> and [False]
False
*Main> and [True]
True

意味がわからない。役に立つのかコレ。
さらにピン。

*Main> and [True, False]
False
*Main> and [True, True]
True

こうだな。

*Main> and [True, True, False]
False

*Main> and [ 1, 1, 0]
:1:13:
    No instance for (Num Bool)
      arising from the literal `0'
    Possible fix: add an instance declaration for (Num Bool)
    In the expression: 0
    In the first argument of `and', namely `[1, 1, 0]'
    In the expression: and [1, 1, 0]

意図がわかった。なるほどー。なるほどねー。