IT練習ノート

IT関連で調べたこと(実際は嵌ったこと)を書いています。

CPS

CPSによる階乗計算

> :{
| fac 0 k = k 1
| fac n k = fac (n-1) $ \res -> k $ (*) n res
| :}
> fac 3 id
6
> fac 3 (+1) -- (1 * 2 * 3) + 1
7

手計算してみる。

  fac 3 k
= fac 2 $ \res -> k $ (*) 3 res
= fac 1 $ \res1 -> (\res -> k $ (*) 3 res) $ (*) 2 res1 
= fac 0 $ \res1 -> (\res -> k $ (*) 3 res) $ (*) 2 res1 
= (\res1 -> (\res -> k $ (*) 3 res) $ (*) 2 res1) 1
= (\res -> k $ (*) 3 res) $ (*) 2 1)
= \res -> k $ (*) 3 res $ 2
= k $ (*) 3 2) 
= k $ 6 

stackでテストスイートを個別に実行する

name: hoge

tests:
  foo-test:
    main:                Spec.hs
    source-dirs:         test
  bar-doctest:
    main: test/doctests.hs

と2つのテストスイートfoo-testbar-doctestを定義する。

このときstack testでテストが実行されるが、両方とも実行される。

片方だけ実行したい場合は

> stack test hoge:foo-test
> stack test hoge:bar-doctest

パッケージが1つだけであれば省略できる。

> stack test :foo-test
> stack test :bar-doctest

Applicative Mabyeで条件分岐

なんらかのデータを取得する処理をして、あればそれを使い、なければ、別途データを取得する

というロジックは、よくあるのではないでしょうか。

例えば、このような場合です。

import qualified Data.HashMap.Lazy as HML

m = HML.fromList [("k1", "**"), ("k2", "##")]

foo x1 x2 = 
  case HML.lookup x1 m of
    Just a1  -> Just a1
    Nothing ->  
      case HML.lookup x2 m of
        Just a2 -> Just a2
        Nothing -> Nothing

キーを二つ用意して、最初のキーで情報が見つかれば、それを使い、見つからなければ、二番目のキーを使います。

lambda > foo "k1" "k2"
Just "**"
lambda > foo "k1_" "k2"
Just "##"
lambda > foo "k1_" "k2_"
Nothing

caseが2つあって冗長です。

Maybeモナドa >>= bとすると、aJustのときに、bに移ります。が、やりたことはこの逆で、Nothingだったらbに移るということです。このような時はApplicativeを使うと良さそうです。

foo'' x1 x2 = HML.lookup x1 m <|> HML.lookup x2 m
lambda > foo'' "k1" "k2"
Just "**"
lambda > foo'' "k1_" "k2"
Just "##"
lambda > foo'' "k1_" "k2_"
Nothing

Contravariant

Data.Functor.Contravariant

covariant     functor ~~> produce something
contravariant functor ~~> consume something
(>$) = contramap . const

const     :: a -> b -> a
contramap :: Contravariant f => (a -> b) -> f b -> f a

contrampa . const = x ->  y -> x 
                         (a -> b) -> f b -> f a
                  = x -> f b -> f a  ( y = a , x = b )
                  = b -> f b -> f a
lambda > import Data.Functor.Contravariant
lambda > data Person = Person {balance :: Int}
lambda > let personBankBalance p = balance p
lambda > let negative = Predicate (\x -> x < 0)
lambda >
lambda > overdrawn = contramap personBankBalance negative ::  Predicate Person
lambda >
lambda > getPredicate overdrawn (Person 5)
False
lambda > getPredicate overdrawn (Person (-10))
True
lambda >
lambda > overdrawn' = (>$) (-15) negative
lambda > getPredicate overdrawn' (Person 5)
True
lambda > getPredicate overdrawn' (Person (-10))
True