Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell ';do';符号_Haskell_Recursion - Fatal编程技术网

Haskell ';do';符号

Haskell ';do';符号,haskell,recursion,Haskell,Recursion,我在阅读本教程时偶然发现了这个定义: 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)

我在阅读本教程时偶然发现了这个定义:

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') 

in3 :: KnightPos -> [KnightPos]  
in3 start = do   
    first <- moveKnight start  
    second <- moveKnight first  
    moveKnight second  
type KnightPos=(Int,Int)
moveKnight::KnightPos->[KnightPos]
moveKnight(c,r)=do
(c',r')[KnightPos]
in3开始=do

首先是一个->[KnightPos]

这样它也将移动次数作为一个参数,而不是仅仅绑定到3次跳跃?

注意,
concatMap moveKnight
具有类型
[Knight]>[Knight]
,并将返回从输入位置可到达的位置

知道这一点,您可以使用:

iterate (concatMap moveKnight)
生成一个无限的位置集合列表,其中下一组位置是通过使骑士从上一组位置移动而获得的

例如:

iterate (concatMap moveKnight) [(1,2)]
  = [ [(1,2)],               -- the initial list
      [(3,1),(3,3),(2,4)],   -- after one iteration
      [(5,2),(1,2),(4,3),(2,3), ... -- after two iterations
      ...
    ]
inNMoves start n = (foldl (>>=) . return) start (replicate n moveKnight)
现在,
in3
可以写成

in3 xs = moves !! 3
  where moves = iterate (concatMap moveKnight) xs

使用直接递归:

inNMoves :: KnightPos -> Int -> [KnightPos]
inNMoves start 0 = return start
inNMoves start n = do
  first <- moveKnight start
  inNMoves first (n - 1)
甚至完全没有意义:

inNMoves = (. flip replicate moveKnight) . foldl (>>=) . return

因为这个练习是关于列表单子的,所以不要去想你所知道的关于列表的内容,而是把你自己限制在单子的结构上。那就是

move :: Monad m => Pos -> m Pos
也就是说,
move
获取一个
Pos
并返回一些关于
Pos
的东西,这些东西在一些一元上下文
m
中。(在列表的情况下,“上下文”是“任意多样性+排序”。但不要去想它)

另外,我们不要在这里谈论
do
,它只是使用
(>>=)
的语法糖分。出于本说明的目的,您需要知道如何使用
(>>=)
转换为表达式

(>>=)
具有签名
ma->(a->mb)->mb
。我们需要的实例是
m Pos->(Pos->m Pos)->m Pos
。你看,我们在这里把
a
b
都实例化为
Pos
。您还可以在此处识别中间部分
(Pos->m Pos)
move
的签名。因此,使用
(>>=)
并将其作为第二个参数
move
,我们可以生成类型为
m Pos->m Pos
的函数

moveM :: Monad m => m Pos -> m Pos
moveM mp = mp >>= move
单子自同态的合成

很明显,
m Pos->m Pos
可以按顺序执行,因为它是从类型到自身的函数(我认为这可以称为monad自同态,因为类型是monad)

让我们写一个函数,它有两个动作

move2M :: Monad m => m Pos -> m Pos
move2M mp = moveM (moveM (mp))
或者采用无点样式(只考虑转换,而不考虑转换的对象):

对于一般情况(由整数
n
参数化的移动数),我们只需要一些由函数链接操作符
连接的
moveM
数。所以如果n是3,我们需要
moveM。莫维姆。moveM
。以下是如何以编程方式执行此操作:

nmoveM :: Monad m => Int -> m Pos -> m Pos
nmoveM n = foldr1 (.) (replicate n moveM)  -- n "moveM"s connected by (.)
这里出现了一个问题:移动0次的结果是什么
foldr1
对于
n
=)的值是未定义的。实际上经常有点不干净,因为它是如此不对称:它需要
ma
a->mb
。换句话说,它有点过于关注转换的对象,而只关注我们案例中的转换。这使得编写转换变得不必要的困难。这就是为什么我们必须加上
。return
:它是从
Pos
m Pos
的初始转换,因此我们可以自由组合任意数量的
m Pos->m Pos

moveM :: Monad m => m Pos -> m Pos
moveM mp = mp >>= move
使用
(>>=)
会产生以下模式:

ma >>= f_1 >>= f_2 >>= ... >>= f_n
其中,
ma
是单数事物,
fi
a->mb
类型的“箭头”(通常a=b)

还有一个更好的变体,
(>=>)
,它将两个
a->mb
类型的箭头按顺序组合在一起,并返回另一个箭头

(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c)
在这里,我们不必关心变换对象,而只关心变换及其组合

现在让我们同意
move
实际上就是这样一个箭头(
Pos->m Pos
)。所以

仍然是类型为
Pos->m Pos
的有效表达式。当使用
(>=>)
时,单子的可组合性变得更加明显

我们可以使用
(>=>)
重新编写
nmoves
,如下所示:

nmoves :: Monad m => Int -> Pos -> m Pos
nmoves n = foldr1 (>=>) (replicate n move)  -- n "move"s connected by >=>
同样,我们使用了
foldr1
,我们要问的是“是什么让0次连续移动”?它必须是同一类型,
Pos->m Pos
,答案是
return

nmoves :: Monad m => Int -> Pos -> m Pos
nmoves n = foldr (>=>) return (replicate n move)
这与我们先前在单子自同态世界中对
nmoves
的定义相比较:我们现在将箭头与
(..
和基格
id
组合,而不是与
(>=>)
和基格
组合返回
。好处是我们不必将给定的
Pos
注入
m Pos

moveM :: Monad m => m Pos -> m Pos
moveM mp = mp >>= move

什么更有意义取决于你的情况,但通常情况下,
(>=>)
(>>=)
干净得多。这是一个很好的解释,但我认为给出单子解释更合适。
move >=> move >=> move >=> move >=> move
nmoves :: Monad m => Int -> Pos -> m Pos
nmoves n = foldr1 (>=>) (replicate n move)  -- n "move"s connected by >=>
nmoves :: Monad m => Int -> Pos -> m Pos
nmoves n = foldr (>=>) return (replicate n move)