The real question is: what do you want to go through the 2D grid?
Is it random access or some kind of template? Problems with dynamic programming are often modeled as moving a 2D grid, but this is not random access, it is pretty with the drawing. And the patterns we can work with.
For example, consider a problem finding the editing distance between two lines where we are given:
-- the cost of replacing one character with another
charEditCost :: Char -> Char -> Int
-- the cost of inserting a character
charInsertCost :: Char -> Int
:
editDistance [] [] = 0
editDistance (a:as) [] = editDistance as [] + charInsertCost a
editDistance [] (b:bs) = editDistance [] bs + charInsertCost b
editDistance (a:as) (b:bs) = minimum
[ editDistance as bs + charEditCost a b
, editDistance (a:as) bs + charInsertCost b
, editDistance as (b:bs) + charInsertCost a
]
- , editDistance as bs
- ,
editDistance (a:as) bs
, - editDistance as (b:bs)
.
:
editDistance as bs = last . last $ grid where
firstRow j = grid !! 0 !! (j-1) + charInsertCost (as!!j)
firstCol i = grid !! (i-1) !! 0 + charInsertCost (bs!!i)
innerCel i j = minimum
[ grid !! (i-1) !! (j-1) + charEditCost (as!!j) (bs!!i)
, grid !! i !! (j-1) + charInsertCost (as!!j)
, grid !! (i-1) !! j + charInsertCost (bs!!j)
]
grid = ( 0 : [ firstRow j | j <- [1..length as] ] ) :
[ ( firstCol i : [ innerCel i j | j <- [1..length as] ] ) | i <- [1..length bs ]
, !!
- O (n). , , ; , . , , , .
, fibs = 1 : 1 : zipWith (+) fibs (tail fibs)
fibs!!(i-1)
fibs!!(i-2)
fibs!!i
,
.
editDistance as bs = last . last $ grid where
firstRow = scanl (+) 0 $ map charInsertCost as
firstCol = scanl (+) 0 $ map charInsertCost bs
grid = ( 0 : firstRow ) : zipWith3 mkRow bs firstCol grid
mkRow b firstCel lastRow = let thisRow = firstCel : zipWith4 (mkCel b) as thisRow lastRow (tail lastRow) in thisRow
mkCel b a leftCel aboveLeftCel aboveCel = minimum
[ aboveLeftCel + charEditCost b a
, aboveCel + charInsertCost b
, leftCel + charInsertCost a
]
2D- , .