> {-# LANGUAGE DeriveFunctor #-}
> import Control.Monad (ap)
前传
和 sxysxy 神犇做了个交♂易,我来发布一些关于 monad 的教程。借用林则徐的两句名言来描述我现在的心情:(这里省略14个字)
树上最近公共祖先 LCA
给定一个有根树(无向无环联通图,其中指定一个特殊节点为**根节点**),给出许多操作 `(u, v)`,求 `u` 到 `v` 的路径上,既是 `u` 的祖先,也是 `v` 的祖先的节点 `lca(u, v)`
从根节点到 `u` 的路径(这条路径是唯一的)上的点都是 `u` 的祖先(包括根和 `u`)
Functor 的 LCA
一个 LCA 的查询系列是一个**世界** `LCA` 里的一个程序,这个程序跑在一个可以求 LCA 的环境中。我们不妨先试试一下只有一个操作的情况。
> data F_LCA a
> = F_LCA
> { f_lcaQuery :: (Int, Int)
> , f_lcaValue :: Int -> a
> }
这个操作可以查询一次 LCA,并用这个 LCA 的结果得到另一个结果。
如果要实现一个可以查询 LCA 的东西,我们可以接受一个 `F_LCA`,并做如下操作:
- 获得 `f_lcaQuery`,即要查询的两个节点 `u` 和 `v`
- 计算得到 `k = lca(u, v)`
- 调用 `f_lcaValue k` 得到最终结果(比如可能是输出的字符串)
可以看到的是,我们的 `LCA a` 中,隐隐地包含着一个类型为 `a` 的最终结果。而这个最终结果,我们是可以直接去改它的:
class Functor f where
fmap :: (a -> b) -> f a -> f b
也就是
fmap :: (a -> b) -> F_LCA a -> F_LCA b
`fmap f qr` 应该将 qr 扩充,其原本结果是 `t` 的,要变成 `f t`。但是在这样的同时,`fmap` 不应修改 `F_LCA` 所进行的询问本身。
> instance Functor F_LCA where
> fmap f (F_LCA q v) = F_LCA q (f . v)
为了支持超过 `fmap` 的东西,显然需要查询 LCA 这个基本操作。
> f_query :: Int -> Int -> F_LCA Int
> f_query x y = F_LCA (x, y) (\u -> u)
然后已经可以用了
ghci> let qr = show `fmap` f_query 2 4
qr :: F_LCA String -- 想查询 LCA,返回一个 String
ghci> f_lcaQuery qr
(2,4) -- 看起来有人想查询 lca(2, 4)
it :: (Int, Int)
ghci> f_lcaValue qr 1 -- 结果是 1,看看怎么样
"1" -- 不错嘛
it :: String
Applicative 的 LCA
为了实现查询(0 次或)多次 LCA,我们自然地想到,可以把 query 变成一个列表,并让 value 接受一个列表,里面放上所有查询结果。
> data A_LCA a
> = A_LCA
> { a_lcaQueries :: [(Int, Int)]
> , a_lcaValue :: [Int] -> a
> }
对最终结果的计算,应该是这样的:
- 获得 `a_lcaQueries`,即要查询的所有节点对
- 计算得到所有 LCA 值
- 调用 `a_lcaValue` 传入所有 LCA 值得到最终结果
显然它还是个 `Functor`
> instance Functor A_LCA where
> f `fmap` (A_LCA q v) = A_LCA q (f . v)
与之前不同的是,我们的 `A_LCA` 可以进行这样的组合:
- 查询一波 LCA 得到一个 String
- 再查询一波 LCA 得到另一个 String
- 然后把这两个 String 拼一块
别忘了咱还能不查询 LCA 直接给出结果
这样,Applicative 简直完美:
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
`pure x` 直接给出结果 x
`f <*> x` 把 `f` 给出的结果 `f1`,和 `x` 组合的结果 `x1` 拼一块,给出结果 `f1 x1`
“拼一块”那个组合,可以这么实现
(pure (++) <*> foo) <*> bar
pure (++) <*> foo <*> bar -- 因为左结合
(++) <$> foo <*> bar -- <$> 就是 fmap
-- 其实是一样的,想想为什么
而 `A_LCA` 的 `Applicative` 实现并不难写
> instance Applicative A_LCA where
> pure x = A_LCA [] (const x)
> A_LCA q1 f <*> A_LCA q2 x
> = let newVal xs
> = case splitAt (length q1) xs of
在这里,我们把列表按长度分成两半。因为我们可以认为,`a_lcaValue` 被调用时传入的列表是可以和查询列表 `a_lcaQueries` 对应的。
> (m, n) -> f m (x n)
> in A_LCA (q1 ++ q2) newVal
当然,基础的操作:
> a_query :: Int -> Int -> A_LCA Int
> a_query x y = A_LCA [(x, y)] (\[u] -> u)
没有问题,也是可以用的
ghci> let qr = (,,) <$> a_query 6 7 <*> a_query 8 10 <*> a_query 9 5
qr :: A_LCA (Int, Int, Int)
ghci> a_lcaQueries qr
[(6,7),(8,10),(9,5)]
it :: [(Int, Int)]
ghci> a_lcaValue qr [1, 2, 3]
(1,2,3)
it :: (Int, Int, Int)
这个对应着什么?当然就是离线 LCA 了。
Monad 的 LCA
每次查询输入的是 `(x, y)`,可惜不一定查询的是 `lca(u, v)`
询问 (u, v) 由下列规则产生(OIER都知道是怎样的吧>_<)
u=min((x+lastans)mod n+1,(y+lastans)mod n+1);
v=max((x+lastans)mod n+1,(y+lastans)mod n+1);
lastans表示上一个询问的答案,一开始lastans为0
当然你知道,更险恶的是这个:
注意出题人为了方便,input的第二行最后多了个空格。
最一般的情况,我们可能每个查询,其实是依赖于前面的结果的。最一般的依赖,是一个函数。
depend :: f a -> (a -> f b) -> f (a, b)
我可以在一个已经造好的程序,后面续上一个函数,从前面程序的结果,映射到我后面需要续上的程序。你得把两个结果都给我。
其实可以直接让你算最后一个值就可以了,显然你前面必须算过才能得出最后这个:
(>>=) :: f a -> (a -> f b) -> f b
用这个可以定义 `depend`
> u `depend` f = u >>= (\k -> (\p -> (k, p)) <$> f k)
现在是 2016 年底了,`Monad` 很只剩下一个必须定义的方法:
class Applicative m => Monad m where
(>>=) :: m a -> (a -> m b) -> m b
对了,一般是 `Monad m` 而不是 `Monad f`。
> data M_LCA a
> = FinalResult a
> | Query (Int, Int) (Int -> M_LCA a)
每一个不是直接给出结果的程序,必定有其第一个查询的 LCA。以后的查询,依赖于这个第一次查询的结果。
我真懒:
> deriving (Functor)
你需要在文件顶上加上一个这个
{-# LANGUAGE DeriveFunctor #-}
每次我告诉你我要 `Query` 的时候,你信心满满地告诉我答案,等待着你的却是更多的 `Query`,直到得到 `FinalResult`。完美。
> instance Applicative M_LCA where
> pure = FinalResult
真・最终结果
> (<*>) = ap
`ap` 我待会儿解释
> instance Monad M_LCA where
> FinalResult a >>= f
如果我们已经知道结果是什么了,lastans 就确定是 `a` 了,把它传进去就行了。
> = f a
> Query uv a >>= f
否则,我们假装已经得到了这一次的查询结果
> = Query uv (\k ->
然后,告诉 `a`,看它返回的续上的程序是什么。我们这就得到了一个少一次查询的 `M_LCA a`,可以递归调用 `(>>=)`
> a k >>= f)
整理一下
instance Monad M_LCA where
FinalResult a >>= f = f a
Query uv a >>= f
= Query uv (\k -> a k >>= f)
基本操作并没有什么不好写的
> m_query :: Int -> Int -> M_LCA Int
> m_query x y = Query (x, y) (\u -> FinalResult u)
不过这个好像没法直接查看查询输出结果了?咱写个 interactive 的。
> run :: M_LCA a -> IO a
> run (FinalResult x) = pure x
> run (Query uv f) = do
> putStrLn $ "Result of " ++ show uv ++ "?"
> r <- read <$> getLine
> run (f r)
`do` 和 `(>>=)` 的对应是这样的
do { x <- e; ... } = e >>= \x -> ...
do { e ; ... } = e >>= \_ -> ...
do { e } = e
do block 简写成一行了,展开大概是这样的
do { a ; b ; c }
= do a
b
c
试试吧,我们先查询 `k = lca(2, 3)`,然后查询 `lca(k, k + 1)`
> m_test :: M_LCA Int
> m_test = do
> k <- m_query 2 3
> m_query k (k + 1)
展开成 `(>>=)` 是这样的:
m_test = m_query 2 3 >>= \k -> m_query k (k + 1)
ghci> run m_test
Result of (2,3)?
54 -- 键盘输入
Result of (54,55)?
0 -- 键盘输入
0 -- 结果输出
看起来不错
为了帮助你理解 `M_LCA`,我们也可以把 m_test 完全展开:
m_test = Query (2, 3) (\k -> Query (k, k + 1) FinalResult)
当然,像这样的就是前面给的那个险恶的东西的处理办法了
foo = Query (x1, y1) (\lastans1 ->
Query ( (lastans + x2) `mod` (n + 1)
, (lastans + y2) `mod` (n + 1))
(\lastans2 -> ...))
你已经成功定义了一个有意思的 Monad 了。
事实上,这个 Monad 用常规方法不好定义,所以咱们在 `M_LCA` 里手动发明了一波 free monad。这个可以以后讲。
练习
- `ap` 在 `Control.Monad` 里面有,对于没用 `(<*>)` 定义了 `(>>=)` 的 Monad,可以用 `ap` 当作 `(<*>)` 的默认实现。(如果用了的话会死循环)类型是非常明确的:
ap :: Monad m => m (a -> b) -> m a -> m b
尝试实现它。当然,你就不能用 `(<*>)` 了(人家等着用 `ap` 当 `(<*>)` 呢)不会的话,倒是有答案:
- 对于全部左结合的 `((a >>= b) >>= c) >>= ...`,算法的复杂度会退化成 `O(n^2)` 的。(好吧,准确地说,每次都会不停地遍历 Query 组成的一长链。)解决这个问题。(你应该是需要修改 `M_LCA` 的定义的。)(提示:这个问题**并不容易**)