Haskell填充列表缺少元素

时间:2015-06-03 08:56:46

标签: list haskell

我正在尝试编写一个函数

fill::Int->Int->[(Int, Int, Int)]->[(Int, Int, Int)] 
fill width height board = ...

“填充”给定的板(在板上的单个(Int,Int,Int)三元组中,前两个Ints是坐标,第三个是该区域的值),其中缺少的(具有第三个坐标)设为0),例如:

let x = fill 3 3 [(1,2,2),(1,3,5),(3,2,3)]

应该导致

x = [(1,1,0),(1,2,2),(1,3,5),(2,1,0),(2,2,0),(2,3,0),(3,1,0),(3,2,3),(3,3,0)].

这里可以使用一些不错的函数,还是按顺序进行一些复杂的双递归?

3 个答案:

答案 0 :(得分:5)

直接的方式可能是(有~O(n ^ 2)成本)

fill :: Int -> Int -> [(Int, Int, Int)] -> [(Int, Int, Int)] 
fill a b defaults = [(x, y, maybe 0 id (search x y defaults)) | x <- [1..a], y <- [1..b]]
           where search _ _ [] = Nothing
                 search x y ((u,v,k):rs) | x == u && y == v = Just k
                                         | otherwise        = search x y rs

但我更喜欢拆分键/值(例如〜(O log n)cost)

import Data.Map hiding (foldr)

-- using key/value
fill' :: Int -> Int -> [((Int, Int), Int)] -> [((Int, Int), Int)]
fill' a b defaults = assocs
                   $ foldr (\(k, v) m -> insertWith (+) k v m) empty
                   $ defaults ++ [((x,y),0) | x <- [1..a], y <- [1..b]]

例如

main = do

    print $ fill 3 3 [(1,2,2),(1,3,5),(3,2,3)]
    print $ fill' 3 3 [((1,2),2),((1,3),5),((3,2),3)]

您的默认值为0,否则,您必须替换(+)上的insertWith。你能想到怎么样?

答案 1 :(得分:5)

我从例如

开始
zeroBoard :: Int -> Int -> [(Int, Int, Int)]
zeroBoard width height = [ (x,y,0) | x <- [1..width], y <- [1..height] ]

然后我们可以使用单个递归,如:

fill::Int->Int->[(Int, Int, Int)]->[(Int, Int, Int)] 
fill width height board = go board $ zeroBoard width height
   where go []             zs                 = zs
         go (t@(x,y,z):bs) (t'@(x',y',z'):zs) =
                if ... 
                then t : go ...
                else t': go ...

您甚至可以跳过zeroBoard中的零,并在go内直接添加0。

答案 2 :(得分:2)

数组API在这里很方便,也为您提供了正确的渐近线。

import Data.Array

fill :: Int -> Int -> [(Int, Int, Int)] -> [(Int, Int, Int)]
fill width height board =
  map (\((x, y), v) -> (x, y, v)) . assocs $
    listArray ((1,1),(width,height)) (repeat 0)
      // map (\(x, y, v) -> ((x, y), v)) board