【问题标题】:How to modify fields of a nested custom data type with lenses, when modifications depend on indices当修改依赖于索引时,如何使用镜头修改嵌套自定义数据类型的字段
【发布时间】:2020-03-05 12:06:52
【问题描述】:

考虑以下几点:

{-# 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 = ?

我如何使用镜头来修改所有Typexs 中的connections 类型为[[Typex]] -&gt; [[Typex]] 的函数,使得在每个Typex

connections = [(level of Typex being modified +1, (x, y))] where
x,y = 0..(length of next [Typex] in [[Typex]])/2

X 和 y 都需要经过下一个 [Typex] 的那个长度。如果可能,最终的 [Typex] 应保持不变。所以同一个[Typex]中每个Typex的所有连接都是一样的。

setConnections $ buildNestedTypexs [(0,1),(1,1)] 的输出应该是:

[ [ 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))] }
  ]]

我想我需要import Control.Lens.Indexed,但仅此而已,因此感谢所有帮助。

【问题讨论】:

  • 一个具体的例子将有助于了解问题到底是什么。
  • @leftaroundabout 我添加了更多解释和预期结果。其中Typexs 与级别 1 的连接保持不变,因为它们是作为参数给出的最后一个 [Typex] 的一部分。
  • 好的,我想现在技术上已经很清楚了,但是......你不能简化/概括一下这个问题吗?这似乎仍然涉及与您所要求的核心无关的特定领域的细节。
  • @leftaroundabout 感谢您使它更具可读性。我从连接中删除了 Double 部分,因为它与问题无关。除此之外,我真的不知道如何在不添加可能使其更难理解的变量的情况下使其更通用。
  • 我们也可以去掉c 参数并完全避免嵌套元组吗?

标签: haskell nested indices haskell-lens custom-data-type


【解决方案1】:

这是你想要的吗?

{-# 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

这不是一个纯粹的镜头解决方案,但我发现作为一般规则使用镜头时,让镜头完成所有工作并不总是一个好主意。它只会让事情变得难以编写和理解。

在这里,我在很多地方都使用了“plain Haskell”:使用手动递归进行模式匹配以处理连续 [Typex]s 的对 x,y,并且我使用了 mapconnect 第一个x :: [Typex] 中的每个Typex 和第二个y :: [Typex]。我还使用map 将新级别添加到坐标列表以生成新的connections 值。

这里使用的唯一镜头表达式是:

  • tx &amp; connections .~ (...)tx :: Typexconnections 字段替换为新值
  • tx ^. level 获取当前tx :: Typex 的级别
  • txs ^.. traverse.coordinate 获取列表Typex 中所有Typex 值的coordinate 字段txs :: [Typex] 并将它们作为列表返回[(Int,Int)]

在我看来,镜头和“普通 Haskell”之间的这种平衡是处理复杂变换的最佳方式。

【讨论】:

  • 我尝试了具有不同输入值的解决方案,它似乎完全可以正常工作。谢谢你。现在我只需要弄清楚它在每个部分中实际做了什么来修改它:)
  • 我添加了一些关于哪些镜头表达式做什么的解释,以防万一。
猜你喜欢
  • 1970-01-01
  • 2015-06-22
  • 1970-01-01
  • 2016-01-20
  • 2016-05-22
  • 1970-01-01
  • 1970-01-01
  • 2021-09-24
  • 2019-01-24
相关资源
最近更新 更多