问题
Considering the following :
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
data Typex = Typex
{ _level :: Int
, _coordinate :: (Int, Int)
, _connections :: [(Int,(Int,Int))]
} deriving Show
makeLenses ''Typex
initTypexLevel :: Int -> Int -> Int -> [Typex]
initTypexLevel a b c = [ Typex a (x, y) [(0,(0,0))]
| x <- [0..b], y <- [0..c]
]
buildNestedTypexs :: [(Int, Int)] -> [[Typex]]
buildNestedTypexs pts
= setConnections [ initTypexLevel i y y
| (i,(_,y)) <- zip [0..] pts
]
setConnections :: [[Typex]] -> [[Typex]]
setConnections = ?
How can I uses lenses to modify the connections in allTypexs with a function of type [[Typex]] -> [[Typex]] in such a way that in each Typex
connections = [(level of Typex being modified +1, (x, y))] where
x,y = 0..(length of next [Typex] in [[Typex]])/2
X and y both need to go through that length of the next [Typex]. The final [Typex] should be left unchanged if possible. So all the connections of each Typex in the same [Typex] are the same.
Output for setConnections $ buildNestedTypexs [(0,1),(1,1)] should be:
[ [ Typex { _level = 0
, _coordinate = (0,0)
, _connections = [(1,(0,0)), (1,(0,1)), (1,(1,0)), (1,(1,1))] }
, Typex { _level = 0
, _coordinate = (0,1)
, _connections = [(1,(0,0)), (1,(0,1)), (1,(1,0)), (1,(1,1))] }
, Typex { _level = 0
, _coordinate = (1,0)
, _connections = [(1,(0,0)), (1,(0,1)), (1,(1,0)), (1,(1,1))] }
, Typex { _level = 0
, _coordinate = (1,1)
, _connections = [(1,(0,0)), (1,(0,1)), (1,(1,0)), (1,(1,1))] }
]
,[ Typex { _level = 1
, _coordinate = (0,0)
, _connections = [(0,(0,0))] }
, Typex { _level = 1
, _coordinate = (0,1)
, _connections = [(0,(0,0))] }
, Typex { _level = 1
, _coordinate = (1,0)
, _connections = [(0,(0,0))] }
, Typex { _level = 1
, _coordinate = (1,1)
, _connections = [(0,(0,0))] }
]]
I suppose I'll need to import Control.Lens.Indexed but that's about it so all help is appreciated.
回答1:
Is this what you want?
{-# LANGUAGE TupleSections #-}
setConnections :: [[Typex]] -> [[Typex]]
setConnections (x:rest@(y:_)) = map (connect y) x : setConnections rest
where connect :: [Typex] -> Typex -> Typex
connect txs tx
= tx & connections .~ (map ((tx ^. level) + 1,) $ txs ^.. traverse.coordinate)
setConnections lst = lst
This isn't a pure lens solution, but I find that as a general rule when working with lenses, it's not always a good idea to get the lenses to do everything. It just makes things difficult to write and hard to understand.
Here, I've used "plain Haskell" in a lot of places: to pattern matching with a manual recursion to process pairs x,y of consecutive [Typex]s and I've used map to connect each Typex in the first x :: [Typex] with the second y :: [Typex]. I've also used map to add the new level to the coordinate list to generate the new connections value.
The only lens expressions used here are:
tx & connections .~ (...)which replaces theconnectionsfield oftx :: Typexwith a new valuetx ^. levelwhich fetches the level of the currenttx :: Typextxs ^.. traverse.coordinatewhich fetches thecoordinatefields of allTypexvalues in the listtxs :: [Typex]and returns them as a list[(Int,Int)]
In my opinion, this sort of balance between lenses and "plain Haskell" is the best way of dealing with complex transformations.
来源:https://stackoverflow.com/questions/60545053/how-to-modify-fields-of-a-nested-custom-data-type-with-lenses-when-modification