Skip to content

Latest commit

 

History

History
273 lines (207 loc) · 7.49 KB

2019-06-26.md

File metadata and controls

273 lines (207 loc) · 7.49 KB

2019-06-26

問題を解く上での諸注意

  • 以下の問題は全て Haskell で解いてください
  • コンパイラは GHC 8.6.5 を用いることとします
  • ビルドツールは cabal または stack を利用してください
  • 問題の提出方法は github のプライベートリポジトリを作成し、そこに全ての内容を含めてください。(また、@waddlaw をリポジトリのコラボレータとして登録してください)
  • また、問題を解く際の制限はありません。Google なり 知人の助けを借りる なり好きにしてください。ただし、不特定多数に対して問題の回答を募ることは不可とします。(例: Twitter で回答を募集する。stackoverflow で回答を募集する等)
  • 問題に誤りが含まれている場合は、それを指摘した上で正しい回答を提示してください。

1. 研究内容 (もしくは現在興味のあること) について述べてください

2. 遅延評価 / 正格評価の利点・欠点について、それぞれ説明してください

3. map を foldr, foldl, unfoldr, fix を用いてそれぞれ再定義してください

-- foldr を使ったバージョン
map1 = undefined

-- foldl を使ったバージョン
map2 = undefined

-- unfoldr を使ったバージョン
map3 = undefined

-- fix を使ったバージョン
map4 = undefined

4. 以下に定義する Free 型について Show, Functor, Applicative, Monad のインスタンスを定義してください

data Free f a
  = Pure a
  | Free (f (Free f a))

5. 以下の自然数の型 Nat が与えられた時、 Nat に対する型レベル Add, Sub, Mul を 定義し、関数 add, sub, mul を定義してください

{-# LANGUAGE DataKinds            #-}
{-# LANGUAGE TypeFamilies         #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE GADTs                #-}
{-# LANGUAGE ScopedTypeVariables  #-}
{-# LANGUAGE StandaloneDeriving   #-}
module Main where

data Nat
  = Zero
  | Succ Nat

type family Add (n :: Nat) (m :: Nat) :: Nat
type family Sub (n :: Nat) (m :: Nat) :: Nat
type family Mul (n :: Nat) (m :: Nat) :: Nat

zero :: SNat Zero
zero = sing
one :: SNat (Succ Zero)
one = sing
two :: SNat (Succ (Succ Zero))
two = sing

-- |
-- 型レベルで 3 + 2 を行うように型を定義してください
add :: SNat (Add (Succ (Succ (Succ Zero))) (Succ (Succ Zero)))
add = sing

-- |
-- 型レベルで 3 - 2 を行うように型を定義してください
sub :: SNat (Sub (Succ (Succ (Succ Zero))) (Succ (Succ Zero)))
sub = sing

-- |
-- 型レベルで 3 * 2 を行うように型を定義してください
mul :: SNat (Mul (Succ (Succ (Succ Zero))) (Succ (Succ Zero)))
mul = sing

main :: IO ()
main = do
  print zero
  print one
  print two
  print add
  print sub
  print mul

-- singleton
data SNat n where
  SZero :: SNat Zero
  SSucc :: SNat n -> SNat (Succ n)

deriving instance Show (SNat n)

class Singleton (n :: Nat) where
  sing :: SNat n

instance Singleton Zero where
  sing = SZero

instance Singleton n => Singleton (Succ n) where
  sing = SSucc (sing :: SNat n)

6. 与えられた論理式がトートロジーかどうか判定する関数を作成してください

論理式を表す Prop 型は以下のように定義されているとします。

data Prop
  = Const Bool
  | Var Char
  | Not Prop
  | And Prop Prop
  | Imply Prop Prop
  deriving Show

具体的な論理式の例

p1 :: Prop
p1 = And (Var 'A') (Not (Var 'A'))

p2 :: Prop
p2 = Imply (And (Var 'A') (Var 'B')) (Var 'A')

p3 :: Prop
p3 = Imply (Var 'A') (And (Var 'A') (Var 'B'))

p4 :: Prop
p4 = Imply (And (Var 'A') (Imply (Var 'A') (Var 'B'))) (Var 'B')

トートロジーかどうか判定する関数 isTaut に、上記の論理式を適用した結果

> isTaut p1
False

> isTaut p2
True

> isTaut p3
False

> isTaut p4
True

7. Boyer-Moore アルゴリズムを実装してください

8. 素朴な Bloom filter を実装してください

9. 以下のライブラリを組み合わせてアプリケーションを作成してください

  • aeson
  • extensible
  • hspec
  • megaparsec
  • optparse-applicative
  • path
  • path-io
  • persistent
  • quickcheck
  • req
  • rio
  • servant
  • yaml

諸注意

  • 作成するアプリケーションは何でも大丈夫です
  • 上記以外のライブラリを含むこともOKです
  • 上記ライブラリの組み合わせ数が多いほど Good

10 スペースリークを含むプログラムの解析と修正を行ってください

module SpaceLeak where

import Data.List
import Data.List.Split

simulate :: [Int] -- 盤面の状況
         -> [Int] -- 次に置く石
         -> [Int] -- 各手番における得点
simulate xs [] = []
simulate xs (m:ms)
  | m `mod` 23 == 0 = let (as,b:bs) = splitAt (length xs - 7) xs
                      in  m+b : simulate (bs++as) ms
  | singleton xs = 0 : simulate (m:xs) ms
  | otherwise = let (as,bs) = splitAt 2 xs
                in 0 : simulate (m:bs ++ as) ms

singleton :: [a] -> Bool
singleton [_] = True
singleton _   = False

runsim :: Int -> Int -> [(Int, Int)]
runsim p m = sort $ flip zip [1..] $ map sum $ transpose $ chunksOf p $ simulate [0] [1..m]

上記のプログラムはスペースリークを含んでいます。

以下の問いに答えてください。

  1. スペースリークが起きていることを確認する方法について説明してください
  2. プログラムを修正し、スペースリークを解消してください
  3. なぜ 2 の方法でスペースリークが修正されるのか説明してください

11. DerivingVia を使った以下のプログラムの動作を説明してください

{-# LANGUAGE DataKinds            #-}
{-# LANGUAGE DeriveGeneric        #-}
{-# LANGUAGE DerivingStrategies   #-}
{-# LANGUAGE DerivingVia          #-}
{-# LANGUAGE KindSignatures       #-}
{-# LANGUAGE ScopedTypeVariables  #-}
{-# LANGUAGE TypeApplications     #-}
{-# LANGUAGE UndecidableInstances #-}

import Data.Aeson

import Data.List (stripPrefix)
import Data.Maybe (fromMaybe)
import Data.Proxy (Proxy (..))
import Data.Text (Text)
import Data.Vector (Vector)
import GHC.Generics (Generic, Rep)
import GHC.TypeLits (Symbol, KnownSymbol, symbolVal)

-- |
-- Strip a prefix from each record field name. Use with via deriving.
newtype StripPrefix (s :: Symbol) a =
  StripPrefix a

instance ( Generic a
         , GFromJSON Zero (Rep a)
         , KnownSymbol s )
      => FromJSON (StripPrefix s a) where
  {-# INLINE parseJSON #-}
  parseJSON = fmap StripPrefix . genericParseJSON options
    where options = defaultOptions { fieldLabelModifier = drop' }
          drop'   = fromMaybe <*> stripPrefix (symbolVal (Proxy @s))
          

data Req = Req
  { reqPath    :: Vector Text
  , reqTimings :: Vector Double
  }
  deriving stock Generic
  deriving FromJSON via StripPrefix "req" Req

repl による動作確認

λ stack repl --package aeson --package vector --package text
> :set -XOverloadedStrings
> Req "dummy" 0
Req {reqPath = "dummy", reqTimings = 0}

> decode "{\"Path\":\"dummy\",\"Timings\":0}" :: Maybe Req
Just (Req {reqPath = "dummy", reqTimings = 0})

なぜ、req という接頭辞が無い json の値をデコードして Req 型の値に変換することができるのか説明できればOKです。

12. クレイトン・M・クリステンセンが提唱するジョブ理論について説明してください