HaskellのArrow

Arrowを学ぶ。Programming with Arrowsを読んでから、「Arrowのはなし」というニコニコ動画をみるのがわかりやすい。イメージは3番目が参考になる。

Arrowを使って奇数列を作ってみる。 0から始まる数列を&&&で分岐させて一方はそのまま戻して、もう一方はtailすることで1から始まる数列にして、合流したら足し合わせる。

Prelude Control.Arrow> let odds = (tail) &&& id >>> (uncurry (zipWith (+)))
Prelude Control.Arrow> take 10 $ odds [0,1..]
[1,3,5,7,9,11,13,15,17,19]

他、参考にしたサイト

RWHではArrowは15.4.2にちょっとだけ触れられている。

ProductName Real World Haskell―実戦で学ぶ関数型言語プログラミング
Bryan O'Sullivan
オライリージャパン / 3990円 ( 2009-10-26 )


Haskellでcat

bindの使い方メモ

import System.Environment

cat:: String -> IO ()
cat = (>>= putStr) . readFile

main = do (file:_) <- getArgs; cat file

追記10.02.13

コメントよりheadを使えば一行で書ける。

main=getArgs>>=readFile.head>>=putStr

Hakellでテンポラリディレクトリやテンポラリファイル

テンポラリファイルをエディタでいじって、保存するとテンポラリファイルの内容を読んでHsSyckでパースして出力する

import System.IO
import System.Process
import System.Directory
import System.Environment
import Control.Monad (liftM)
import Data.Yaml.Syck

main :: IO ()
main = do
  tmpdir <- getTemporaryDirectory
  (pathOfTempFile, h) <- openTempFile tmpdir "temp.yaml"
  editor   <- liftM (lookup "EDITOR") getEnvironment
  case editor of Just ed -> (>>= waitForProcess) . runCommand $ ed ++ " " ++ pathOfTempFile
                 Nothing -> error "command error\n"
  hClose h

  inpStr <- readFile pathOfTempFile
  yamlstr <- parseYaml inpStr
  print yamlstr

  removeFile pathOfTempFile
  return ()

HoogleGHCの標準ライブラリドキュメントが手放せない身体になってきた。

ProductName Real World Haskell―実戦で学ぶ関数型言語プログラミング
Bryan O'Sullivan
オライリージャパン / 3990円 ( 2009-10-26 )


もっとHaskellの本出ないかなぁ。Principles of Biomedical InformaticsのHaskell阪みたいなのとか。

ProductName Principles of Biomedical Informatics
Ira J. Kalet PhD
Academic Press / 8335円 ( 2008-10-15 )


HaskellでPitを使う

perlにもConfig::Pitあるし、Pythonにもあるしという状況なので、Haskellでネットワークのちょろっとしたものを書くときにもパスワードをハードコードしないで、pitのやつを使いたかった。

module HsPit (
            pitGet,
            pitSet
           )
where

import System.FilePath ((</>))
import System.Directory
import Data.Yaml.Syck

pitGet :: String -> IO [(String,String)]
pitGet query = do
  home <- getHomeDirectory
  do yaml <- parseYamlFile (home </> ".pit" </> "default.yaml")
     case n_elem yaml of
       EMap list -> return $ map (\keyval -> (,) ((fromYaml . fst) keyval) ((fromYaml . snd) keyval)) $ concatMap (fromEMap . snd) $ filter checkElem list
           where checkElem ynode = ((n_elem . fst) ynode) == (EStr (packBuf query))
       otherwise -> return []
       where 
         fromYaml MkNode {n_elem=EStr str}   = unpackBuf str
         fromEMap MkNode {n_elem=EMap nodes} = nodes

pitSet = error "Not implemented"

HsSyckの使い方はPugsのYamlコードを参考にした。

で、例えばtwitterでつぶやくときには

import Network.HTTP
import Network.URI
import Codec.Binary.Base64.String
import Data.Maybe
import HsPit

tweet username password msg = simpleHTTP req where 
    req = Request uri POST [ah] "" where
        ah = Header HdrAuthorization $ "Basic " ++ encode (username ++ ":" ++ password)
        uri = fromJust $ parseURI $ "http://twitter.com/statuses/update.xml?" 
              ++ urlEncodeVars [("status", msg)]

main = do 
  userdata <- pitGet "twitter"
  case lookup "username" userdata of Just username ->
                                         case lookup "password" userdata of Just password ->
                                                                                tweet username password "tWeet"

なんかごちゃごちゃとしてしまった。

HaskellでYAML

HaskellでYAMLを扱うライブラリはyaml,HsSyckがあるんだけどlibyamlのバインディングであるyamlのほうは使い方がわからん。

なので、HsSyckの使い方を覚えた。

import Data.Yaml.Syck

global_tag  = mkNode $ EStr $ packBuf "Item 1"
name_tag    = mkNode $ EStr $ packBuf "name"
name_value  = mkNode $ EStr $ packBuf "kzfm"
email_tag   = mkNode $ EStr $ packBuf "address"
email_value = mkNode $ EStr $ packBuf "xxx@gmail.com"
pass_tag    = mkNode $ EStr $ packBuf "password"
pass_value  = mkNode $ EStr $ packBuf "snail"

item = mkNode $ EMap [(name_tag,name_value),(email_tag,email_value),(pass_tag,pass_value)]
node = mkNode $ EMap [(global_tag,item)]

main = do
  emitYamlFile "test.yaml" node

作成されたtest.yamlの中身

--- 
? "Item 1"
: 
  ? "name"
  : >-
    kzfm

  ? "address"
  : >-
    xxx@gmail.com

  ? "password"
  : >-
    snail

これをperlでparseしてみる

use YAML;
my $filename = "test.yaml";
my $doc = YAML::LoadFile($filename);
print YAML::Dump($doc);

実行結果

---
Item 1:
  address: xxx@gmail.com
  name: kzfm
  password: snail

ナイス!

hoogleをローカルで

昨晩、String -> ByteStringに変換する関数がわかんね的なことをtweetしたらHoogeleで型検索出来ることを教えてもらった。

Hoogle 激ヤバ!マスト!!!!(ローカルに)

というわけで、ローカルで検索できるように

sudo cabal install hoogle

これだけ。

~/.cabal/binにhoogleっていうコマンドがインストールされるのでパス通しておく。

$ hoogle "String -> ByteString"
Data.ByteString.Char8 pack :: String -> ByteString
Data.ByteString.Lazy.Char8 pack :: [Char] -> ByteString
Prelude read :: Read a => String -> a
Text.Read read :: Read a => String -> a
Data.String fromString :: IsString a => String -> a
...

これはアンセムといっても間違いない。

Haskellのプログラムの中でエディタを使ってファイルをいじる

週末はニューラルネットの実装でもするつもりでいたのに、突然

Haskellのプログラムの中でファイルをエディットして保存したら、そのファイルの中身を読む

ということをやりたくなってしまった。要するにHaskellでこれがやりたくなった

import System.FilePath ((</>))
import System.Directory
import System.Process
import System.Environment
import Control.Monad (liftM)

main = do 
  home     <- getHomeDirectory
  (file:_) <- getArgs
  editor   <- liftM (lookup "EDITOR") getEnvironment
  case editor of Just ed -> (>>= waitForProcess) . runCommand $ ed ++ " " ++ (home </> file)
                 Nothing -> error "command error\n"
  inpStr <- readFile $ home </> file
  print inpStr

最初、

runCommand $ ed ++ " " ++ (home </> file)

って書いてて、あーブロックされてないんだろうなぁと。探したらどう書く?にあった。

あと、RWHのモナド変換子の章を読むべし的な流れになってる。

ProductName Real World Haskell―実戦で学ぶ関数型言語プログラミング
Bryan O'Sullivan,John Goerzen,Don Stewart
オライリージャパン / ¥ 3,990 ()
在庫あり。

アッカーマン関数をHaskellで

計算論を読んでたらアッカーマン関数が出てたのでHaskellで書いてみた。

ack 0 n = n + 1
ack m 0 = ack (m-1) 1
ack m n = ack (m-1) $ ack m (n-1)

定義をそのまま書き下せばいい。

*Main> ack 1 2
4
*Main> ack 3 2
29
*Main> ack 3 4
125

それにしても計算論難しい。

ProductName 計算論 計算可能性とラムダ計算 (コンピュータサイエンス大学講座)
高橋 正子
近代科学社 / ¥ 3,570 ()
在庫あり。

Haskellでルンゲ・クッタ法

さきのオイラー法よりも効率の良いRunge-Kutta法をHaskellで

runge h f  p = runge'
    where runge' = p:zipWith runge'' runge' [h*x| x <- [1..]] 
              where runge'' x t = x + (k1 + 2*k2 + 2*k3 + k4)/6
                        where
                          k1 = h * f x t
                          k2 = h * f (x + k1/2) (t + h/2)
                          k3 = h * f (x + k2/2) (t + h/2)
                          k4 = h * f (x + k3)  (t +h)

f x t = x

刻み幅がそんなに小さくなくても、かなりいい精度が出ていることがわかる。

*Main> let eu = runge 0.1 f 1
*Main> eu !! 10
2.718279744135166

テーラー展開はこれが参考になるというか、展開をするというイメージをつかみやすくなると思う。

ProductName 「無限と連続」の数学―微分積分学の基礎理論案内
瀬山 士郎
東京図書 / ¥ 2,625 ()
在庫あり。

Haskellでオイラー法を実装する

オイラー法とはあれです、常微分方程式解くやつ。

これをHaskellで実装してみた。下の二行。

euler h f = euler'
    where euler' = (f 1.0 0.0): zipWith (+) euler' [h*y| y <- (zipWith f euler' [h*x| x <- [1..]])]

f = xにすればx = e**tが解になるので。

f _ 0.0 = 1.0
f x t = x

として解いてみる。

*Main> euler 0.00001 f !! 100000
2.7182510547639454

あんま刻み幅を小さくしすぎるのはだめ。

*Main> euler 0.000001 f !! 1000000
*** Exception: stack overflow