Maybe/Option のリストから普通のリストへ

Ocaml の時は自作したけど、ScalaHaskell は結構簡単にできる。

Scala

scala> List(Some(4), Some(2), None, Some(9), None, Some(0)) flatMap (x => x)
res0: List[Int] = List(4, 2, 9, 0)

Haskell

> [Just 4, Just 2, Nothing, Just 9, Nothing, Just 0] >>= maybe [] return
[4,2,9,0]


ただ、Haskell は普通に bind id しようとすると怒られる。

> [Just 4, Just 2, Nothing, Just 9, Nothing, Just 0] >>= id

<interactive>:1:55:
    Couldn't match expected type `[b]' against inferred type `Maybe t'
    In the second argument of `(>>=)', namely `id'
    In the expression: [Just 4, Just 2, Nothing, Just 9, ....] >>= id
    In the definition of `it':
        it = [Just 4, Just 2, Nothing, ....] >>= id


for/do 構文を使っても書ける。
実際、TDDBC 名古屋初日の、自分の卓の Scala 組は for 式を上手いこと使っていた。


OCaml ももっと自由に使えるように勉強していこう。

liftとか

liftで遊んでみる。

> :t map
map :: (a -> b) -> [a] -> [b]
> :t fmap
fmap :: (Functor f) => (a -> b) -> f a -> f b
> :t liftM
liftM :: (Monad m) => (a1 -> r) -> m a1 -> m r
> :t liftM2
liftM2 :: (Monad m) => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
> :t ap
ap :: (Monad m) => m (a -> b) -> m a -> m b

> :t liftM (+)
liftM (+) :: (Num a1, Monad m) => m a1 -> m (a1 -> a1)
> :t liftM2 (+)
liftM2 (+) :: (Num a1, Monad m) => m a1 -> m a1 -> m a1
> :t liftM2 id
liftM2 id :: (Monad m) => m (a2 -> r) -> m a2 -> m r

> liftM2 (+) [1..4] [3,4]
[4,5,5,6,6,7,7,8]
> liftM (+) [1..4] `ap` [3,4]
[4,5,5,6,6,7,7,8]
> ap [succ,(*2)] [1..4]
[2,3,4,5,2,4,6,8]

> :t liftM (+) [1..4]
liftM (+) [1..4] :: (Num a1, Enum a1) => [a1 -> a1]

> map (\f -> f 10) (liftM (+) [1..4])
[11,12,13,14]
> liftM (+) [1..4] >>= return . (\f -> f 10)
[11,12,13,14]

do みたいな for

Scala の for を Haskell の do みたいに使ってみる。

Haskell

f :: Maybe Int -> Maybe Int -> Maybe Int
f am bm = do
 a <- am
 b <- bm
 let c = a + b
 return (2 * c)
> f (Just 3) (Just 1)
Just 8
> f Nothing Nothing
Nothing
> f (Just 3) Nothing
Nothing
> f Nothing (Just 1)
Nothing


Scala

scala> def f(am:Option[Int], bm:Option[Int]) = for {
     |   a <- am
     |   b <- bm
     |   c = a + b
     | } yield {
     |   2 * c
     | }
f: (am: Option[Int],bm: Option[Int])Option[Int]

scala> f(Some(3),Some(1))
res0: Option[Int] = Some(8)

scala> f(None,None)
res1: Option[Int] = None

scala> f(Some(3),None)
res2: Option[Int] = None

scala> f(None,Some(1))
res3: Option[Int] = None

おー。

foldを使ってfoldを作る

foldr/foldl を使って、foldl/foldrを作ってみる。

myFoldL :: (a -> b -> a) -> a -> [b] -> a
myFoldL f y xs = foldr (\x g a -> g $ f a x) id xs y

myFoldR :: (a -> b -> b) -> b -> [a] -> b
myFoldR f y xs = foldl (\g x a -> g $ f x a) id xs y

-- 間違い : 適用が右から
myFoldLb :: (a -> b -> a) -> a -> [b] -> a
myFoldLb f y xs = foldr (\x g a -> f (g a) x) id xs y
-- 間違い : 適用が左から
myFoldRb :: (a -> b -> b) -> b -> [a] -> b
myFoldRb f y xs = foldl (\g x a -> f x $ g a) id xs y
> foldl (flip (:)) [0] [1..3]
[3,2,1,0]
> myFoldL (flip (:)) [0] [1..3]
[3,2,1,0]
> myFoldLb (flip (:)) [0] [1..3]
[1,2,3,0]
> foldr (:) [0] [1..3]
[1,2,3,0]
> myFoldR (:) [0] [1..3]
[1,2,3,0]
> myFoldRb (:) [0] [1..3]
[3,2,1,0]

第2要素の時の展開 (x = 2, g = (3:)/(1:), a = [0]) まで辿ってようやく分かった・・・

Yコンビネータ

フィボナッチと階乗やってみる。

> let y f = f $ y f
> :i y
y :: (b -> b) -> b      -- Defined at <interactive>:1:4
> y (\f n -> if n < 2 then n else f (n-1) + f (n-2)) 7
13
> y (\f n -> if n < 2 then n else f (n-1) + f (n-2)) 100
Interrupted. ← (´・ω・`)ショボーン

> y (\f (a,b) m -> if m < 2 then a else f (b, a + b) (m - 1)) (1,1) $ 7
13
> y (\f (a,b) m -> if m < 2 then a else f (b, a + b) (m - 1)) (1,1) $ 100
354224848179261915075

フィボナッチ。

> y (\f n -> if n < 2 then n else n * f (n-1)) 10
3628800
> y (\f n -> if n < 2 then n else n * f (n-1)) 100
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
> y (\f a b -> if b < 2 then a else f (a*b) (b-1)) 1 $ 10
3628800
> y (\f a b -> if b < 2 then a else f (a*b) (b-1)) 1 $ 100
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

階乗。

RWH 4章練習問題1

なんか作るのにすごい時間かかった・・・

  • リストを述語で分割
splitWith :: (a -> Bool) -> [a] -> [[a]]
splitWith f [] = []
splitWith f xs = case span f xs of
 (a,b) -> a : (splitWith f $ snd $ span (not . f) b)
> splitWith (/= 'a') "igaiahaidihoihihoaofha"
["ig","i","h","idihoihiho","ofh"]
  • テキストの行列入れ替え
import Data.List
inversionStr = unlines . transpose . fill . lines
 where
  fill xss = addBranks (maxLength xss) xss
--  maxLength = foldl (\a b -> max a $ length b) 0
  maxLength = maximum . map length
--  addBranks n [] = []
--  addBranks n (xs:xss) = (addBrank n xs) : (addBranks n xss)
  addBranks n = map (addBrank n)
  addBrank n xs = xs ++ (brank $ n - length xs)
--  brank 0 = []
--  brank n = ' ':(brank $ n - 1)
  brank n = take n $ repeat ' '
> putStrLn $ inversionStr "hello\nworld\naaa\nbbbbb\nccccccc\ndd\n"
hwabcd
eoabcd
lrabc
ll bc
od bc
    c
    c
  • おまけ

zipWithN を作ってみる。

import Data.List
zipWithN f = map (foldl1 f) . transpose
> zipWithN (+) [[1,2,3],[4,5,6],[7,8,9]]
[12,15,18]

transpose すげぇ。

リストとか

Scalaでやってたやつとか。

  • 平方数
> take 10 $ zipWith (*) [1..] [1..]
[1,4,9,16,25,36,49,64,81,100]
> take 10 $ map (\x -> x * x) [1..]
[1,4,9,16,25,36,49,64,81,100]
  • 総和と総積
> foldl (+) 0 [1..10]
55
> foldl (*) 1 [1..10]
3628800
> foldl1 (+) [1..10]
55
> foldl1 (*) [1..10]
3628800
fizzbuzz = map f [1..]
 where
  f x = case (mod x 3, mod x 5) of
   (0,0) -> "FizzBuzz"
   (0,_) -> "Fizz"
   (_,0) -> "Buzz"
   _ -> show x
> take 20 fizzbuzz
["1","2","Fizz","4","Buzz","Fizz","7","8","Fizz","Buzz","11","Fizz","13","14","FizzBuzz","16","17","Fizz","19","Buzz"]

インデントにはまるなどしていた。

中置関数

map とかって、中置で使うとOO系言語と逆になるんだよなぁ・・

> :info map
map :: (a -> b) -> [a] -> [b]   -- Defined in GHC.Base
> (\x -> x + 1) `map` [1..10]
[2,3,4,5,6,7,8,9,10,11]
> succ `map` [1..10]
[2,3,4,5,6,7,8,9,10,11]
> let myMap a b = map b a
> [1..10] `myMap` (\x -> x + 1)
[2,3,4,5,6,7,8,9,10,11]
> [1..10] `myMap` succ
[2,3,4,5,6,7,8,9,10,11]

意味的には、「関数 map リスト」の方が合ってるのか・・・?

Haskellやる!

Real World Haskell―実戦で学ぶ関数型言語プログラミング が研究室に入ったので、本格的に Haskell やってみようかと。

とりあえず、3章まで読んだ。
目指せリア充!
(注:「Real World Haskell充」の略)

> let (+++) a b c = a + b + c
> 2 +++ 4 $ 5
11

(第一項) (演算子) (第二項) $ (第三項) 的な。

myFact n = fact n primes
 where
  primes = primes' [2..]
  primes' (x:xs) = x : (primes' $ filter (\a -> mod a x /= 0) xs) 
  fact n (pn:pns)
   | n == 1 = [] 
   | (floor $ sqrt $ fromInteger n) < pn = [n] 
   | mod n pn == 0 = pn : (fact (div n pn) (pn:pns)) 
   | otherwise = fact n pns 

where かっけぇ!
参考:Haskell vs Ruby: Netsphere Laboratories