【发布时间】:2016-07-31 19:38:38
【问题描述】:
假设我有一些像这样组织在网格中的数据(尺寸可能会有所不同,但网格的一侧总是n**2):
0 1 2 3
4 5 6 7
8 9 A B
C D E F
我想要实现的是拥有一个列表,其中包含以不同方式表示的相同数据,即拆分为列、行或(最重要的)单元格
0 1 | 2 3
4 5 | 6 7
----+----
8 9 | A B
C D | E F
因此,如果我执行某些操作,我将能够获取以下列表中的数据:
[[0, 1, 4, 5],
[2, 3, 6, 7],
[8, 9, C, D],
[A, B, E, F]]
在哪里订购无关紧要。
我想稍后用它来构建一个镜头,它能够根据不同类型的表示来设置值。这可以通过在命令式语言中使用指针或引用来实现(如果适用)。
除了细节之外,我想知道是否有一种通用方法可以让相同的内部数据以不同的方式表示。
这是我目前得到的,使用[Int] 作为内部表示,并使用转换函数来获得特定的“视图”:
import Data.List (transpose)
data Access = Rows | Columns | Cells
isqrt :: Int -> Int
isqrt = floor . sqrt . fromIntegral
group :: Int -> [a] -> [[a]]
group _ [] = []
group n l
| n > 0 = (take n l) : (group n (drop n l))
| otherwise = error "inappropriate n"
representAs :: [Int] -> Access -> [[Int]]
representAs list Rows = group (isqrt . length $ list) list
representAs list Columns = transpose $ list `representAs` Rows
representAs list Cells = let row_width = isqrt . length $ list
cell_width = isqrt row_width
drops = map (\x -> cell_width
* row_width
* (x `quot` cell_width)
+ cell_width
* (x `rem` cell_width)
) [0..row_width-1]
in (map ( (map snd)
. (filter ( (==0)
. (`quot` cell_width)
. (`rem` row_width)
. fst)
)
. (zip [0..])
. (take (row_width * cell_width))
. (`drop` list)
) drops
)
main = mapM_ (putStrLn . show) ([1..16] `representAs` Cells)
我的问题基于与this one 相同的想法,但那里的答案仅涉及内存问题,而不是构造问题。此外,如果我要在几个表示中以不同的方式存储相同的数据,据我所知,我将不得不更新所有这些数据以设置新值。
【问题讨论】:
-
我不会使用列表来表示这一点,这将是非常低效的。此外,您真的应该制作一个镜头,而不是以不同的表示形式显示数据 - 这将允许您使用数据的不同视图查看和修改矩阵,而无需进行不必要的转换。例如
Access -> Lens' (Array (Int, Int) a) (Array Int a)或Access -> Traversal' (Array (Int, Int) a) a -
@user2407038 我认为只有在我提供某种 setter 和 getter 后才能构建镜头,例如使用 @AlexeyKuleshevich 提出的
index函数作为 getter 和相应的 setter。是否有可能以其他方式做到这一点?
标签: haskell data-representation