首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >哈斯克尔:一元论

哈斯克尔:一元论
EN

Stack Overflow用户
提问于 2015-08-17 12:18:20
回答 2查看 239关注 0票数 6

我正在学习一些Haskell,我对这些单子有一些麻烦,我了解它们,知道它们是关于什么的,但是在这个特殊的例子中,我有一些问题。当我在LYAH上学习它时,我遇到了一个练习,就是用一个骑士计算你在三个动作中可以达到的位置(从国际象棋游戏中),我们使用列表单数如下:

假设,

代码语言:javascript
复制
type KnightPos = (Int,Int)

moveKnight :: KnightPos -> [KnightPos]
moveKnight (c,r) = do
   (c',r') <- [(c+2,r-1),(c+2,r+1),(c-2,r-1),(c-2,r+1)
              ,(c+1,r-2),(c+1,r+2),(c-1,r-2),(c-1,r+2)
              ]
   guard (c' `elem` [1..8] && r' `elem` [1..8])
   return (c',r')

这是可行的,如果我把我的位置给这个函数,它成功地计算了可能的未来位置,但是现在我想在其中实现Writer monad,这样我就可以检索我是如何到达这个位置的。所以我做了这个功能,

假设,

代码语言:javascript
复制
type KnightRoute = Writer [KnightPos] KnightPos

moveKnight' :: KnightPos -> [KnightRoute]
moveKnight' (c,r) = do
   (c',r') <- [(c+2,r-1),(c+2,r+1),(c-2,r-1),(c-2,r+1)
              ,(c+1,r-2),(c+1,r+2),(c-1,r-2),(c-1,r+2)
              ]
   guard (c' `elem` [1..8] && r' `elem` [1..8])
   return $ toKr (c',r') (c,r)
 where toKr pos oldpos = Writer (pos,[oldpos])

如果我给它一个KnightPos,它可以工作,但是使用monads,我不能从一个KnightRoute中提取一个KnightPos来执行函数。

代码语言:javascript
复制
*Main> let a = moveKnight' (2,4) !! 0
*Main> runWriter a
((4,3),[(2,4)])
*Main> a >>= moveKnight'

<interactive>:4:7:
Couldn't match type ‘[]’ with ‘Writer [KnightPos]’
Expected type: KnightPos -> Writer [KnightPos] KnightRoute
  Actual type: KnightPos -> [KnightRoute]
In the second argument of ‘(>>=)’, namely ‘moveKnight'’
In the expression: a >>= moveKnight'

我理解为什么它不起作用,我从我的作者中提取了(4,3),然后我把这个给了KnightPos'。但是KnightPos'返回一个KnightRoute列表,其中我需要一个KnightRoute,这是一个逻辑错误,但我不知道该如何做。有什么简单的方法可以用单簧管来完成吗?

(预先谢谢:)

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-08-17 13:37:25

在哈斯克尔,这种“两人结合”是一件非常普遍的事情。幸运的是,该语言足够灵活,我们可以很好地抽象这一点。

从数学上讲,你想要的是一个composition of two functors。而不是这种新类型,这通常用变压器的概念来表达:而不是直接使用Writer monad,而是使用WriterT monad转换器。WriterT w [] abasically the same作为[Writer w a],所以在您的示例中可以使用:

代码语言:javascript
复制
import Control.Monad.Trans.Class
import Control.Monad.Trans.Writer

moveKnight'' :: KnightPos -> WriterT [] [KnightPos] KnightPos
moveKnight'' (c,r) = do
   (c',r') <- lift [(c+2,r-1),(c+2,r+1),(c-2,r-1),(c-2,r+1)
                   ,(c+1,r-2),(c+1,r+2),(c-1,r-2),(c-1,r+2)
                   ]
   guard (c' `elem` [1..8] && r' `elem` [1..8])
   tell [(c,r)]
   return (c',r')
票数 6
EN

Stack Overflow用户

发布于 2015-08-17 15:55:34

你可以写

代码语言:javascript
复制
a' :: Int -> KnightRoute
a' i = a >>= \p -> moveKnight' p !! i

在这里,i用于消除作者的内部列表。而且,由于懒惰,你可以把Int -> a变成[a]

代码语言:javascript
复制
asList :: (Int -> a) -> [a]
asList f = map f [1..]

那么a'的所有路由列表是

代码语言:javascript
复制
a's :: [KnightRoute]
a's = asList a'

把所有东西拼凑在一起:

代码语言:javascript
复制
moveKnight :: KnightRoute -> [KnightRoute]
moveKnight k = map (\i -> k >>= \p -> moveKnight' p !! i) [1..]
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32050222

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档