てくのろじーたのしー

Haskellぺろぺろ

WordPressで作られたサイトにログインする

やりたいことはタイトルそのまま。haskellで書く。

使うパッケージはhttp-contuid 2.1.8とutf8-string 1.0.1.1(stackのlts-3.14)

{-# LANGUAGE OverloadedStrings #-}
import qualified Data.ByteString.Lazy.UTF8 as U
import Network.HTTP.Conduit

ap = [("account", "name"), ("pwd", "password")]

main = do 
  man <- newManager tlsManagerSettings
  log' <- parseUrl "http://piyopiyo/wp-login.php"
  let log= urlEncodedBody ap log'
  cookies <- httpLbs log man >>= return . responseCookieJar

  req' <- parseUrl "http://piyopiyo/target.html"
  let req = req' { cookieJar = Just cookies }
  httpLbs req man >>= return . U.toString . responseBody >>= putStrLn

mainの前半でセッション情報を含んだCookieを取得して、後半でそれを使ってログインしていないと見れないページを取得。

requestBodyにログイン情報を乗せて失敗してハマった。urlEncodedBodyを使うのがポイント。

「関数型」というジャンル

多くのプログラミング言語関数型プログラミング要素が取り込まれるようになって久しいです。

  • Java8でラムダ式が導入された時は大きな話題になりました。
  • C#では2.0でジェネリクスが使えるようになり、3.0ではラムダ式型推論(のようなもの)も使えるようになりました。
  • ClojureScalaでは基本的にイミュータブルな値を使うコーディングが推奨され、関数合成も使います。時には遅延評価も使います。
  • Rubyのブロックは実質、高階関数ですね。
  • Shemeでは末尾再帰最適化が言語仕様として要求されます。

一部、関数型と言うにはやや強引なものもありますがラムダ式ジェネリクス型推論、イミュータブル、関数合成、遅延評価、高階関数、末尾再帰最適化、モナドなど関数型プログラミングには多くの側面があります。

今回は「関数型とは何か」のような話はしません。関数型というある意味バズワードとなってしまった言葉への一つの考え方を話します。

突然ですが私はEDMやハードコアミュージックが好きです。techno-tanoCというハンドルネームもHARDCORE TANO*Cが元ネタです。ハードコアテクノと一口に言ってもその分類はたくさんあり、wikiには現在12種類の分類が載っていますがこれもハードコアテクノの分類の一部に過ぎません。MainStreamGabbaやBrostepなどのようにここ数年生まれたような音楽ジャンルもあります。ジャンルの移り変わりの速さや新しいジャンルの発生の多様性はIT業界と同じかそれ以上と思います。

そんな音楽の世界でジャンルとは何なのでしょうか?BPS(beat per second)や使う音の傾向で分類されることが多いですが、これから外れる曲も多いです。一番無難な分類法は作曲者による宣言でしょう。youtubeニコニコ動画に上げられる多くの曲にはタイトルにジャンルが書かれていることが多く、「この曲は作曲者がこのジャンルだと言っているからこのジャンルだ」と考えることです。もちろん聞く人によって「この曲はこのジャンルの方が適切ではないか」ということはあると思いますし、私も時々「この曲は自分の思うジャンルの傾向と違う」と思うことはありますがそれを批難する人は原理主義者ぐらいでしょう。

閑話休題、「関数型」というのもこれに似たものではないでしょうか? 先ほど挙げた特徴を以って言語を「関数型」と分類することは簡単です。しかし「関数型」には明確な定義がありません。従って個々人の思う「関数型」には差異があります。これは言語設計者にも言えることです。「関数型」言語あるいは「関数型」の機能を持った言語は言語設計者の思う「関数型」を体現したものです。

時々「その定義だと○○という言語は関数型ではなくなる」「その定義だと△△という言語まで関数型言語になってしまう」という言葉を見かけますが、それはその言語設計者が思う「関数型」との差異があったというだけで、言語設計者を絶対視した原理主義者の言葉に他なりません。

「関数型」という単語は一定のコンセンサスを以って用いるには便利ですが、定義がない以上乱用は混乱を招きます(言語設計者も例外ではありません)。誰かを絶対視した原理主義者にはなりたくないものです。

Rubyで正規表現を使った抽出

rubyって楽で良いですよね。今日もその良さを感じたのでお裾分け。

最初にマッチした部分を取得したいことってよくありますよね。

文字列の中からfooの後に連なった最初の数字を取得したいとします。冗長に書くとこんな感じですかね?

if match = "foo123bar456".match(/foo(\d+)/)
  digit = match[1]
end

String#matchを使ってもString#=~を使ってもどっちみち$1などは作られるのでさっさと=~と$1を使ったコードにしましょう。マッチを行う場所とマッチした部分を使う場所が遠いでもない限りあまり問題にはならないことが多いでしょう。でも良い子のみんなは時と場合を選びましょう。

if "foo123bar456" =~ /foo(\d+)/
  digit = $1
end

こんなコード書こうものなら熟練rubyistからのマサカリが飛んできそうです。すばやく後置ifに書き換えます。

digit = $1 if "foo123bar456" =~ /foo(\d+)/

これでifの外からでもdigitが使えますね。これで熟練rubyistもニッコリです。

 

 

 

 

 

いやいや、コードを左から読んでいると真っ先に$1という部分が来て見た目がなんかアレです。

digit = "foo123bar456" =~ /foo(\d+)/ && $1

はい。みんな大好き&&さんです。後置ifとどちらを使うかは時と場合を選んでください(二度目)。ruby使い以外からしたらトリッキーなコードですね。

 

え?マッチしなかった時はどうする?一行でif-then-end書いた方がマシ?

みんな違ってみんな良いということですね!!!

寒いノリでくだらないネタ書いたことを反省している。夜のテンション怖い。

Haskellで正規表現

Haskell正規表現を使った覚書。

  • 最初にマッチした部分を取得
firstCapture :: String -> String -> String
firstCapture source pattern = head . mrSubList $ source =~ pattern

firstCapture "abcdefg" =~ "a(\\w\\w)d\\w\\w) == "bc"

matchResultでは最初にマッチしたものだけを補足するので、[[String]]でマッチさせて、探した方が良いかも。 括弧が1つならhead . head $ source =~ pattern

"123abc" =~ "\\d\\d\\d\\w\\w\\w"

\\を付ける。

(.) . (.) の型

初めましての方が多いかもしれません。にわかプログラマのtechno_tanoCです。

前にふとghciで

:t (.) . (.)

と打ち込んでみたところ、

(.) . (.) :: (b -> c) -> (a -> a1 -> b) -> a -> a1 -> c

と表示されました。

にわかの私はこれだけでもう大混乱です。(b -> c)って何だ!(a -> a1 -> b)ってどこから来たんだ!

どういう超自然的法則によってこのような型になっているのか理解できなかった、というか今でもあまりよく分かっていませんが、自分の理解を深めるためにも頑張って説明してみます。

どこかしら間違えている可能性が高いのでマサカリやツッコミ、罵倒をして頂けると喜びます。


何はともあれとりあえず (.) の定義を確認してみます。

HackageのPreludeを見てみると、 (.) は

(.)    :: (b -> c) -> (a -> b) -> a -> c
(.) f g = \x -> f (g x)

のように定義されていました。

(b -> c)という型のfと、(a -> b) という型のgを受け取って、\x -> f (g x)しています。


まずは(.) . (.)の真ん中の . をこの定義から変形してみます。

(.) . (.)
-- ↓
\f -> (.) ((.) f)

(.)は第一引数としてb -> cという型の関数を要求していますので、xではなくfと書いてみました。
ということでfはj -> kという型です。(定義のa, b, cと紛らわしいのでj, kとします)


\f -> (.) ((.) f) の一つ目の(.)は前置記法になっているので、ラムダ式を使ってこれを中置記法にしてみます。

\f -> (.) ((.) f)
-- ↓
\f -> (\p -> ((.) f) . p)
-- ↓ 括弧を外す
\f -> \p -> ((.) f) . p
-- ↓ 二引数関数にしてもHaskellなら勝手にカリー化してくれるので同じはず
\f p -> ((.) f) . p

p が何者なのかはちょっと置いておいて、見た感じでは「fとpを取って、((.) f)とpを合成する関数」のように見えます。


f は j -> k という型なので、「(. :: (j -> k) -> (i -> j) -> i -> k) f」 は「 (.) に f を部分適用したもの」で、型は (i -> j) -> i -> k です。


(i -> j) -> i -> kと何か謎のpを合成するのですが、
定義では(.)は (b -> c) -> (a -> b) -> a -> cという型で、定義での(b -> c)の部分には \f p -> ((.) f) . pにおける (.) fの(i -> j) -> i -> kという型で決まってしまっています。

つまり

(以下自信無い)


bの部分が(i -> j)で、cが(i -> k)ということになる(はず)。

したがって(b -> c)の部分の型は(i -> j) -> (i -> k)です(多分)。


さて、ここでpはどのような型でなければならないでしょうか。

     b   ->    c

(i -> j) -> (i -> k)

のように対応しているので、

a ->  b

より、p は

m -> (i -> j)

である必要があります。


よって

\f p -> ((.) f) . p :: (j -> k) -> (m -> (i -> j)) -> m -> i -> k

です。

(m -> (i -> j))は括弧を外して (m -> i -> j)になり、

\f p -> ((.) f) . p :: (j -> k) -> (m -> i -> j) -> m -> i -> k

これは、最初の

(.) . (.) :: (b -> c) -> (a -> a1 -> b) -> a -> a1 -> c

と同じ形です。


説明終わり。


なんというか説明というより、「こうやったらなんか知らないけど同じ形になった」というだけな感じもしますが、今回は以上です。

(a -> a1 -> b)の部分は「関数合成の都合上、受け渡し先が関数を要求してるから関数を返す関数、つまり二引数関数だったから」ということなのかな?