从三元组列表打印2D网格

时间:2014-04-15 10:21:35

标签: haskell

我想逐行打印三元组列表。该列表包含三元组,三元组的每个第一个元素应放在作为tirples的第二个和第三个元素给出的坐标中。网格从(0,0)坐标开始。 下面是一个2x2网格示例:

generateGrid [('a',0,0),('b',0,2),('c',1,1), ('d',2,2)]
-> a - b
   - c -
   - - d

我有一种生成此功能的方法,但我无法将所有内容都放在代码中。 我试图找到三元组中的最大数字,并按其递增的值创建网格。通过这种方式,我可以从(0,0)cooradinate开始。然后,我想查看所有三元组并将第一个元素放在相关坐标中。 我怎样才能采用这种方法?

以下是我的代码:

gridMax ((p1, p2, p3):xs) = max (maximum(secList ((p1, p2, p3):xs))) (maximum(thirdList ((p1, p2, p3):xs)))

secList [] = []
secList ((p1, p2, p3):xs) = [p2] ++ secList xs

thirdList [] = []
thirdList ((p1, p2, p3):xs) = [p3] ++ thirdList xs

这样我就找到了网格的最大值,这意味着我应该从(0,0)开始创建一个(max + 1)X(max + 1)网格。我无法获得剩下的代码。

2 个答案:

答案 0 :(得分:1)

由于Haskell的懒惰,我们可以假设我们有一个无限网格,所以我们可以根据参数中逐个给出的坐标在这个网格上设置单元格,并在此过程中记录结果网格的大小。当我们完成时,我们可以从无限网格中获取结果网格。

以下是这个想法的实现:

setVal :: Int -> a -> [a] -> [a]
setVal idx val lst = h ++ (val : tail t)
    where (h, t) = splitAt idx lst

setCell :: (Char, Int, Int) -> [[Char]] -> [[Char]]
setCell (c, x, y) grid = setVal x xl grid
    where xl = setVal y c $ grid !! x

generateGrid :: [(Char, Int, Int)] -> [[Char]]
generateGrid cs = take (mx+1) $ map (take (my+1)) grid
    where (mx, my, grid) = foldr step (0, 0, g) cs
          g = repeat $ repeat '-'
          step co@(c, x, y) (mx, my, g) =
            let g' = setCell co g
                mx' = max x mx
                my' = max y my
            in
                (mx', my', g')

可以这样测试:

*Main> let cs = [('a',0,0),('b',0,2),('c',1,1), ('d',2,2)] :: [(Char, Int, Int)] 
*Main> putStr $ unlines $ generateGrid cs
a-b
-c-
--d

答案 1 :(得分:0)

只是为了好玩,这里是Haskell One-Liner:

generateGrid :: [((Int, Int), Char)] -> String 
generateGrid xs = 
  unlines $ map (map snd) $ groupBy ((==) `on` fst . fst) $ assocs $ accumArray (const id) '-' ((0,0), (maximum $ map (fst . fst) xs, maximum $ map (snd . fst) xs)) xs

让我们解码这里发生的事情:

generateGrid :: [((Int, Int), Char)] -> String 
generateGrid xs = 
  unlines .                         
  map (map snd) .
  groupBy ((==) `on` fst . fst) . 
  assocs .
  accumArray (const id) 
             '-' 
             ((0,0), (maximum $ map (fst . fst) xs, maximum $ map (snd . fst) xs))       
  $ xs

我们要做的第一件事是使用accumArray :: (e -> a -> e) -> e -> (i, i) -> [(i, a)] -> Array i e创建一个数组。它需要一个默认的'元素 - 这里有'-',原因很明显;一个组合函数,这里是const id - 它只返回第二个元素,它将是从列表中提取的元素;范围,从(0,0)开始,以最大值结束;最后,xs输入列表,这是与值(Int, Int)相关联的键Char列表。

这一点的全部意义是"填写" ((x,y),'-')的空格。 accumArray是一种非常简单的方法。

然后我们使用assocs从数组中取回一个列表。

下一个函数将获取关联列表并将它们分组到子列表中 - 每个子列表包含一行。

现在我们有一个行列表 - 我们不再对索引感兴趣,所以map (map snd)摆脱了它们。最后,unlines将行组合成一个由换行符分隔的单个字符串。

相关问题