Data structures 如何用函数式语言实现网格?
我在函数式语言中以不同的方式实现常量网格。一个完美的解决方案应该以每一步的伪常量时间提供遍历,而不是使用命令式构造(惰性是可以的)。仍然欢迎不完全满足这些要求的解决方案 我的建议基于这样的四路链接节点 一个基本的操作是构造一个给定大小的网格。似乎该操作将确定类型,即哪些方向将是惰性的(显然,没有惰性就无法实现此数据结构)。因此,我建议(在OCaml中) 参照顺序为:左、上、右、下。左上角暂停。然后按对角线方向构建网格 下面是一个Data structures 如何用函数式语言实现网格?,data-structures,functional-programming,ocaml,lazy-evaluation,self-reference,Data Structures,Functional Programming,Ocaml,Lazy Evaluation,Self Reference,我在函数式语言中以不同的方式实现常量网格。一个完美的解决方案应该以每一步的伪常量时间提供遍历,而不是使用命令式构造(惰性是可以的)。仍然欢迎不完全满足这些要求的解决方案 我的建议基于这样的四路链接节点 一个基本的操作是构造一个给定大小的网格。似乎该操作将确定类型,即哪些方向将是惰性的(显然,没有惰性就无法实现此数据结构)。因此,我建议(在OCaml中) 参照顺序为:左、上、右、下。左上角暂停。然后按对角线方向构建网格 下面是一个make_grid函数,它使用坐标元组作为节点值来构造给定大小的
make_grid
函数,它使用坐标元组作为节点值来构造给定大小的网格。请注意,gl
,gu
,gr
,gd
函数允许在网格上向所有方向行走,如果给定GNil
,将返回GNil
let make_grid w h =
let lgnil = Lazy.from_val GNil in
let rec build_ur x y ls dls = match ls with
| l :: ((u :: _) as ls') ->
if x = w && y = h then
GNode ((x, y), l, u, GNil, GNil)
else if x < w && 1 < y then
let rec n = lazy (
let ur = build_ur (x + 1) (y - 1) ls' (n :: dls) in
let r = gd ur in
let d = gl (gd r)
in GNode ((x, y), l, u, r, d)
)
in force n
else if x = w then
let rec n = lazy (
let d = build_dl x (y + 1) (n :: dls) [lgnil]
in GNode ((x, y), l, u, GNil, d)
)
in force n
else
let rec n = lazy (
let r = build_dl (x + 1) y (lgnil :: n :: dls) [lgnil] in
let d = gl (gd r)
in GNode ((x, y), l, u, r, d)
)
in force n
| _ -> failwith "make_grid: Internal error"
and build_dl x y us urs = match us with
| u :: ((l :: _) as us') ->
if x = w && y = h then
GNode ((x, y), l, u, GNil, GNil)
else if 1 < x && y < h then
let rec n = lazy (
let dl = build_dl (x - 1) (y + 1) us' (n :: urs) in
let d = gr dl in
let r = gu (gr d)
in GNode ((x, y), l, u, r, d)
)
in force n
else if y = h then
let rec n = lazy (
let r = build_ur (x + 1) y (n :: urs) [lgnil]
in GNode ((x, y), l, u, r, GNil)
)
in force n
else (* x = 1 *)
let rec n = lazy (
let d = build_ur x (y + 1) (lgnil :: n :: urs) [lgnil] in
let r = gu (gr d)
in GNode ((x, y), l, u, r, d)
)
in force n
| _ -> failwith "make_grid: Internal error"
in build_ur 1 1 [lgnil; lgnil] [lgnil]
它构造一个节点,给定当前位置x
和y
、前一个对角线ls
的暂停元素列表、前一个对角线urs
的暂停元素列表。名称ls
来源于ls
上的第一个元素是当前节点的左邻居。构建下一条对角线需要urs
列表
build\u urs
功能继续在右上角对角线上构建下一个节点,以暂停方式通过当前节点。左邻居和上邻居来自ls
,右邻居和下邻居可以通过对角线上的下一个节点访问
注意,我在urs
和ls
列表中放了一堆GNil
s。这样做是为了始终确保build\u-ur
和build\u-dl
可以使用这些列表中的至少两个元素
构建ur“>
build\u dl
函数的工作原理类似
对于这样一个简单的数据结构,这个实现似乎过于复杂。事实上,我很惊讶它能工作,因为我在编写它的时候受到了信念的驱使,无法完全理解它为什么能工作。因此,我想知道一个更简单的解决方案
我正在考虑按行构建网格。这种方法边界情况较少,但我无法消除在不同方向构建后续行的需要。这是因为当我以一行结束并想从头开始构建另一行时,我必须知道当前ro中第一个节点的向下节点w、 在我从当前函数调用返回之前,我似乎不知道这一点。如果我不能消除双向性,我将需要两个内部节点构造:一个具有挂起的左侧和顶部,另一个具有挂起的右侧和顶部
另外,这里是这个实现的要点以及省略的函数:如果您想要一个功能解决方案,您正在寻找的数据结构是a。我已经用Haskell编写了其余的代码,因为我觉得它更符合我的口味,但它很容易移植到OCaml。没有交叉注释
{-#语言记录通配符#-}
模块网格在哪里
导入数据,也许吧
我们可以从理解列表的数据结构开始:你可以把拉链想象成列表深处的一个指针。你可以看到watherver在你指向的元素的左边,然后是你指向的元素,最后是右边的任何东西
类型a=([a],a[a])
给定一个列表和一个整数n
,您可以将焦点放在位于n
位置的元素上。当然,如果n
大于列表的长度,那么您就失败了。需要注意的一点是,列表的左侧部分是向后存储的:因此,在con中可以将焦点移到左侧持续时间。向右移动也是如此
focusListAt::Int->[a]->可能(lista)
focusListAt=go[]
哪里
没什么
go acc 0(hd:tl)=仅(acc,hd,tl)
go acc n(hd:tl)=go(hd:acc)(n-1)tl
现在让我们转到网格。网格将只是一个行列表
newtype Grid a=Grid{unGrid::[[a]]}
网格
的拉链现在由一个网格给出,该网格表示当前焦点上方的一切,另一个网格表示当前焦点下方的一切,还有一个列表拉链(高级:请注意,这看起来有点像嵌套的列表拉链,可以用更通用的术语重新定义)
数据网格=
gridzippers{over::Grid a
,下图::网格a
,左::[a]
,右::[a]
,焦点::a}
通过首先聚焦于右边的行,然后聚焦于右边的元素,我们可以将网格
聚焦于某些坐标x
和y
focusGridAt::Int->Int->Grid a->Maybe(Grid a)
聚焦网格x y g=do
(前、行、后)没有
(hd:tl)->只要$g{focus=hd,left=tl,right=focus:right}
goRight::Gridzippers a->Maybe(Gridzippers a)
戈赖特g@GridZipper{..} =
案件权利
[]->什么都没有
(hd:tl)->只要$g{focus=hd,left=focus:left,right=tl}
在上移或下移时,我们必须小心一点,因为我们需要将注意力集中在我们在新行中留下的那一行的正上方(或下方)。我们还必须将我们关注的前一行重新组合到一个好的旧列表中(通过将反向的左添加到焦点:右)
goUp::gridzippers a->Maybe(gridzippers a)
goUp gridzippers{..}=do
let make_grid w h =
let lgnil = Lazy.from_val GNil in
let rec build_ur x y ls dls = match ls with
| l :: ((u :: _) as ls') ->
if x = w && y = h then
GNode ((x, y), l, u, GNil, GNil)
else if x < w && 1 < y then
let rec n = lazy (
let ur = build_ur (x + 1) (y - 1) ls' (n :: dls) in
let r = gd ur in
let d = gl (gd r)
in GNode ((x, y), l, u, r, d)
)
in force n
else if x = w then
let rec n = lazy (
let d = build_dl x (y + 1) (n :: dls) [lgnil]
in GNode ((x, y), l, u, GNil, d)
)
in force n
else
let rec n = lazy (
let r = build_dl (x + 1) y (lgnil :: n :: dls) [lgnil] in
let d = gl (gd r)
in GNode ((x, y), l, u, r, d)
)
in force n
| _ -> failwith "make_grid: Internal error"
and build_dl x y us urs = match us with
| u :: ((l :: _) as us') ->
if x = w && y = h then
GNode ((x, y), l, u, GNil, GNil)
else if 1 < x && y < h then
let rec n = lazy (
let dl = build_dl (x - 1) (y + 1) us' (n :: urs) in
let d = gr dl in
let r = gu (gr d)
in GNode ((x, y), l, u, r, d)
)
in force n
else if y = h then
let rec n = lazy (
let r = build_ur (x + 1) y (n :: urs) [lgnil]
in GNode ((x, y), l, u, r, GNil)
)
in force n
else (* x = 1 *)
let rec n = lazy (
let d = build_ur x (y + 1) (lgnil :: n :: urs) [lgnil] in
let r = gu (gr d)
in GNode ((x, y), l, u, r, d)
)
in force n
| _ -> failwith "make_grid: Internal error"
in build_ur 1 1 [lgnil; lgnil] [lgnil]
build_ur :
int -> int ->
(int * int) grid Lazy.t list ->
(int * int) grid Lazy.t list -> (int * int) grid
type 'a cell = {
x: int; (* position on the horizontal axis *)
y: int; (* position on the vertical axis *)
value: 'a;
}
type 'a grid = {
cells: (int * int, 'a cell) Hashtbl.t;
init_cell: int -> int -> 'a;
}
let create_grid init_cell = {
cells = Hashtbl.create 10;
init_cell;
}
let hashtbl_get tbl k =
try Some (Hashtbl.find tbl k)
with Not_found -> None
(* Check if we have a cell at the given relative position *)
let peek grid cell x_offset y_offset =
hashtbl_get grid.cells (cell.x + x_offset, cell.y + y_offset)
(* Get the cell at the given relative position *)
let get grid cell x_offset y_offset =
let x = cell.x + x_offset in
let y = cell.y + y_offset in
let k = (x, y) in
match hashtbl_get grid.cells k with
| Some c -> c
| None ->
let new_cell = {
x; y;
value = grid.init_cell x y
} in
Hashtbl.add grid.cells k new_cell;
new_cell
let left grid cell = get grid cell (-1) 0
let right grid cell = get grid cell 1 0
let down grid cell = get grid cell 0 (-1)
(* etc. *)