基本的Haskell函数

时间:2014-09-06 08:58:24

标签: haskell

我是Haskell的新手,我正在努力学习基础知识。我需要声明一个名为Pos的类型,它将具有两个整数,然后我需要方向北,南,西,东,以便我可以根据方向改变位置。 一旦我这样做,我需要创建一个称为移动的函数,它将获取移动列表和初始位置,并在所有移动后返回位置。 这是我的代码,但我仍然坚持我必须遍历移动列表。

type Pos = (Int, Int)
data Direction = North | South | East | West

move :: Direction -> Pos -> Pos
move North (x,y) = (x, y+1)
move West (x,y) = (x-1, y)
move South (x,y) = (x, y-1)
move East (x,y) = (x+1, y)

moves :: [Direction] -> Pos -> Pos
moves [] (x,y) = (x,y)
moves (h:xs) (x,y) 
        | h == North = move North (x,y)
        | h == West  = move West (x,y)
        | h == South = move South (x,y)
        | otherwise  = move East (x,y)

我在这里缺少什么?

2 个答案:

答案 0 :(得分:9)

将一系列事物组合成一个东西称为折叠列表。 (大约)有两个列表折叠:foldlfoldr。倾向于使用折叠是学习Haskell的重要一步。我们需要foldl,其类型为

foldl :: (a -> b -> a) -> a -> [b] -> a

现在foldl使用组合函数和初始值来组合列表中的东西,例如

foldl (£) s [x,y,z] = (((s £ x) £ y) £ z)

l中的foldl是左边的缩写,因此可以帮助您记住,您的起始值s将在左侧结束,但更重要的是,括号为与左边相关联。)

第三个参数是列表参数[b]。我们会将其用于移动列表,因此b类型将为Direction

第二个参数是a类型的起始值,因此我们将其用于您的初始位置,因此a类型将为Pos

第一个参数是将列表中的内容与当前值组合在一起的函数。现在我们知道类型baDirectionPos,我们知道我们的合并函数必须具有类型Pos -> Direction -> Pos。移动函数几乎正是我们需要的,除了我们需要交换参数。 flip函数执行此操作,因此flip move具有我们需要的类型。

因此我们将foldl的类型专门化为

foldl :: (Pos -> Direction -> Pos) -> Pos -> [Direction] -> Pos

并定义

moves :: [Direction] -> Pos -> Pos
moves ds i = foldl (flip move) i ds

现在foldl有一个名为foldl'的“严格”版本,在这种情况下更快,所以如果你在快节奏的游戏中使用它,或者处理大量的动作,你想要使用那个。

与往常一样,您可以通过在hoogle上搜索其姓名或类型来查找功能。

还有一个折叠函数,它以不同的方式折叠列表。您可以在this question中了解它们之间的区别。简而言之,foldr的工作原理如下:

foldr (?) s [x,y,z] = (x ? (y ? (z ? s)))

r中的foldr是对的缩写,所以它可以帮助您记住您的起始值s将在右侧结束但更重要的是括号是与权利相关联。)

答案 1 :(得分:3)

在对应h的单个移动之前或之后,您需要对其余的moves进行{em>递归调用Direction。如果之前,那么每个案例都会变成:

move North (moves xs (x,y))

如果你希望它在之后,这可能是给出问题规范的正确方法,那么它将是:

moves xs (move North (x,y))

然而,重复moves中的案例分析有点不必要,因为您可以直接使用move拨打h,例如

moves (h:xs) (x,y) = moves xs (move h (x,y))

事实上,您甚至不需要在(x,y)元组上明确地进行模式匹配;

moves (h:xs) pos = moves xs (move h pos)

正如另一个答案所指出的,这与内置foldl函数(直到参数顺序)相同,但最好先了解如何手动完成。