2013-05-05 49 views
5

我想在Haskell中实现Ravi Sethi的小被子语言。塞西的小被子的概述可以在这里看到:http://poj.org/problem?id=3201Ravi Sethi在Haskell的小被子语言

下面是我迄今为止功能:

import Data.List.Split 

rotate :: Int -> [a] -> [a] 
rotate n xs = iterate rot xs !! n 
    where 
     rot xs = last xs : init xs 

turn :: [a] -> [a] 
turn x = rotate 2 x 

grid :: Int -> [String] -> String 
grid n = unlines . map concat . chunksOf n 

printAtom :: [String] -> IO() 
printAtom x = putStrLn $ grid 2 x 

我实现rotateturn功能使用,因为它只是旋转的名单n时间在左边。

下面是一个例子原子:

let a0 = ["#", "@", "#", "#"] 

为了说明原子被如何看待,我将使用printAtom功能:

printAtom a0 

#@ 
## 

当我打电话turn上原子a0,并打印所得到的原子,我结束以下(turn应代表90度顺时针转到整个原子):

## 
#@ 

这是第一轮的预期输出。这将对应于面向原子a1。上原子a1一转应产生:

@# 
## 

然而,鉴于turn功能的制约,它简单地返回原子回a0状态。为了解决这个问题,我想实现一个功能,newTurn,使用基于使用chunksOf 2 atom测试卫士,如下所示:

newTurn :: [a] -> [a] 
newTurn x 
| chunksOf 2 x == [["#", "@"], ["#", "#"]] = rotate 2 x 
| chunksOf 2 x == [["#", "#"], ["#", "@"]] = rotate 1 x 
| chunksOf 2 x == [["@", "#"], ["#", "#"]] = rotate 2 x 
| chunksOf 2 x == [["#", "#"], ["@", "#"]] = rotate 1 x 

我几乎可以肯定,我不理解如何使用警卫,和我绝对知道我不太明白放在函数定义上的类型约束。当我尝试导入newTurn功能分为ghci中,我得到这个错误:

functions.hs:19:29: 
Couldn't match type `a' with `[Char]' 
    `a' is a rigid type variable bound by 
     the type signature for newTurn :: [a] -> [a] at functions.hs:18:1 
In the expression: "#" 
In the expression: ["#", "@"] 
In the second argument of `(==)', namely `[["#", "@"], ["#", "#"]]' 

我的问题的那冗长的解释之后,基本上就是我需要知道的是我可以改变我的turn功能代表一个原子顺时针转90度的实际值? (注:这是第一个项目,我已经试过在Haskell来解决,所以我相信我的代码是相当混乱。)

回答

9

我们先来关注转弯。对于一个原子[a, b, c, d],要求它grid 2用于打印产量

a b 
c d 

转到其顺时针旋转90°将导致

c a 
d b 

它来自列表[c, a, d, b]。所以顺时针转动不是列表元素的循环交换。如果只有2×2个原子将需要考虑,使用平面列表的turn自然实施将

turn [a,b,c,d] = [c,a,d,b] 
turn _   = error "Not an atom" 

但是,根据概述,事情并不那么简单,你可以缝被子,所以你可以获得任何尺寸的棉被m×n,其中mn均匀。所以使用扁平列表代表被子并不是最好的主意。

假设你代表的被子作为一个列表的列表,每行一个列表,所以例如

[ [a,b,c,d] 
, [e,f,g,h] ] 

2×4被子。旋转的顺时针旋转90°产生的4×2被子

[ [e,a] 
, [f,b] 
, [g,c] 
, [h,d] ] 

现在,有没有什么直接做的标准库,但是,在Data.List,我们有transpose,其中变换2×4被子上面到

[ [a,e] 
, [b,f] 
, [c,g] 
, [d,h] ] 

而且我们然后半有:

turn = map reverse . transpose 

据概述,在tu人们还需要旋转符号'\' becoems '/',反之亦然,'-'变成'|',反之亦然。这可以通过在所有行上映射一个turnChar :: Char -> Char函数来实现。

+0

我在定义转为函数时遇到问题。如果我说'let turn = map reverse。转置'并运行':t turn',它给了我'turn :: [[a]] - > [[a]]'。当我使用它作为函数定义文件中的'turn'的类型签名时,出现以下错误:'无法与实际类型a0 - > c0'匹配预期类型[[a]] – mrg1023 2013-05-05 23:58:39

+0

我无法诊断完全没有看到代码,但是这个消息看起来好像你正在传递一个函数来转向。像“转弯”或任何东西。你能否在某个地方发布确切的问题部分? – 2013-05-06 00:03:00

+0

接受你的建议,'turn = map reverse。转置“,它在解释器中传递列表列表时工作。以下是我如何将它定义为一个函数:'turn :: [[a]] - > [[a]] turn xs = map reverse xs。转置xs'。这给了我以前的评论中显示的类型错误。 – mrg1023 2013-05-06 00:14:02

2

下面是一个例子原子:

["A", "B", "C", "D"] 

这里是你如何显示它:

AB 
CD 

这里的问题是,自然的方式来旋转(一维)列表(弹出一个元素关闭一端,将其推到其他)不旋转的方式2x2平方。

我建议使用不同的数据结构来表示原子。例如您可以将一个原子表示为列表清单:

[["A", "B"], ["C", "D"]] 
+0

甚至:'数据quilt a = Quilt {topLeft :: a,topRight :: a,bottomLeft :: a,bottomRight :: a}' – dflemstr 2013-05-05 21:51:59

+0

@dflemstr是的,但是这并不会推广到他想要的时候将原子缝合在一起(尽管列表清单可能不会),而且我很累,没有精力来解释他为什么要这样做(甚至在某处找到预先写好的解释)。 – dave4420 2013-05-05 21:57:35