我正在尝试编写一个函数
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)].
这里可以使用一些不错的函数,还是按顺序进行一些复杂的双递归?
答案 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