簡體   English   中英

在haskell中創建類似網格的數據類型

[英]Making a grid-like data type in haskell

問題

我一直在想如何在一段時間內有效地完成這項任務,但由於某種原因,我無法做到這一點。 我需要建模一個矩形網格,其中每個字段包含一些數據。

我需要通過拉鏈訪問它,我的焦點是一個字段(值可以這么說)。 拉鏈應該支持goDowngoUpgoLeftgoRight ,每個操作都將焦點更改為指示方向的字段,以及here應返回當前焦點字段的值。

雖然這可以通過Map來完成,但是在更改焦點需要log n time的意義上是低效的, nMap中元素的數量,因為Map具有對數查找時間。

我需要指定的行動在O(1)時間內工作。

為了便於說明,請查看下面的矩陣。 帶括號的數字是當前焦點。

1 (2) 3
4  5  6
7  8  9

如果我申請goRight ,我應該得到:

1  2 (3)
4  5  6
7  8  9

如果我現在在here申請,返回的值應為3

如上所述的表單上的數據類型如何在haskell中查找? 它是否可以作為代數數據類型實現?

請記住,在所有四個方向上的焦點變化應該在O(1)時間內可行,以及讀取當前焦點的值。

好吧,我很失望沒有其他人對這個問題給出了“正確”的答案,因為我知道它存在,但我無法解釋它。 我的答案基於http://blog.sigfpe.com/2006/12/evaluating-cellular-automata-is.html

首先,一個標准,即1d拉鏈可以是:

Data U x = U [x] x [x]

第一個元素是所有元素的反向列表“左”焦點,然后焦點元素然后列出所有元素“右”焦點。 例如:

U [-1,-2,-3] 0 [1,2,3]

然后我們可以左右移動拉鏈。 當我們跑掉網格的邊緣時,你必須決定做什么。 原始帖子簡單地假設一個無限網格,以便將角落案例作為練習留給讀者。

left  (U (a:as) x zs) = U as a (x:zs)
right (U as x (z:zs)) = U (x:as) z zs

現在看起來像容器的一切都應該是一個Functor,所以:

instance Functor U where
  fmap f (U a x z) = U (map f a) (f x) (map f z)

在這一點上我真的希望別人能夠跳進去解釋我將要做的事情和原因。 我將使U成為Control.Comonad一個實例。 我能解釋的最好的是comonads是一種由內到外的monad。 而不是給你一個元素,並要求你創建一個具有新值的容器(>>= :: Monad m => ma -> (a -> mb) -> mb) ,Comonads給你整個結構,只詢問對於屬於焦點的值: (=>>) :: Comonad w=>wa -> (wa -> b) -> w

所以在comonad-3.0.2包中使用Control.Comonad的術語:

Instance Comonad U where
  -- extract :: U a -> a   -- called coreturn in the post
  extract (U _ x _) = x

  -- duplicate :: U a -> U (U a)  -- called cojoin in the post
  duplicate x = U (tail $ iterate left x) x (tail $ iterate right x)

復制品為您提供拉鏈拉鏈,每個拉鏈左或右移動一個元素,然后是最后一個元素。 它似乎是一個巨大的內存,但Haskell是懶惰的,實際的內存占用非常小,如果你不環顧四周,整個集合的O(n)和O(1)的順序。

但這只是一個方面。 再次出於原因,我不夠聰明,無法解釋將此擴展到兩個維度,這很簡單:

data U2 x = U2 (U(U x))

instance Functor U2 where
  fmap f (U2 y) = U2 $ fmap (fmap f) y

instance Comonad U2 where
  extract (U2 y) = extract (extract y)
  duplicate (U2 y) = fmap U2 $ U2 $ roll $ role y where
    iterate' f = tail . iterate f
    role x = U (iterate' (fmap left) x) x (iterate' (fmap right) x)

復制函數現在創建一個網格網格,每個網格都適當移位。 所以

goLeft  u = let (U _ (U x _ _) _) = duplicate u in x
goRight u = let (U _ (U _ _ x) _) = duplicate u in x
goUp      = left  . duplicate
goDown    = right . duplicate
here      = extract

因為Haskell很懶,所有這些都是O(1)函數。 更有趣的是,您可以here更改時間和內存中的O(1)成本,並在計算中使用鄰域單元格。 這使得像game of life細胞自動機這樣的東西變得如此簡單

rule  (U2 (U
      (U (u0:_) u1 (u2:_):_)
      (U (u3:_) u4 (u5:_))
      (U (u6:_) u7 (u8:_):_))) =
         let n = length $ filter id [u0,u1,u2,u3,u5,u6,u7,u8] in
           u4 && (n==2 || n==3) || (not u4) && n==3

-- assume u is the original graph each step is
step u = u =>> rule

除了上面的博客文章,我建議在Google上搜索Comonad以了解更多內容,特別是因為我不是最好的解釋這些內容。

這可能不是你要求的,但我想聽聽為什么首先提出一個更好的答案。

data GridWithZipper a = GridWithZipper { grid :: [[a]]
                                       , gwzx :: Int
                                       , gwzy :: Int
                                       }

goLeft  gwz = gwz { gwzx = gwzx gwz - 1 }
goRight gwz = gwz { gwzx = gwzx gwz + 1 }
goUp    gwz = gwz { gwzy = gwzy gwz - 1 }
goDown  gwz = gwz { gwzy = gwzx gwz + 1 }

get gwz = grid gwz !! gwzx gwz !! gwzy gwz

所有操作顯然都是 O(1)

所有go操作都是O(1) ,但是獲取和設置是O(sqrt(n))

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM