2013-08-28 33 views
6

我試圖執行包含元素列表的嵌套數據結構的操作。經過各種方法的磨合後,我終於決定將鏡頭作爲實現這一目標的最佳方式。它們完美地用於查找和修改結構的特定元素,但到目前爲止,我很難理解如何添加新元素。使用鏡頭在特定位置插入列表

從我讀過的內容來看,我不能在技術上使用遍歷,因爲它違反了遍歷法則以將新元素插入到列表中,假設我甚至可以弄清楚如何在Traversal中使用遍歷第一名(我在Haskell方面仍然很弱,鏡頭包裝中大部分東西的類型簽名讓我頭暈)。

具體而言,我試圖完成的是,在匹配特定選擇器的元素列表中找到某個元素,然後在匹配元素之前或之後插入一個新元素(該函數的不同參數比賽之前或之後)。 Control.Lens是否已經有能夠完成我想要做的事情,並且我對類型簽名的理解太弱,無法看到它?有沒有更好的方法來完成我想要做的事情?

如果我只是試圖向列表的開頭或結尾添加一個新元素,但將其插入某個特定中間位置是困難的部分,那將是相當微不足道的。在我寫的一些預鏡片代碼中,我用摺疊來實現我想要的東西,但它開始在結構的更深層嵌套部分(EG摺疊在摺疊內部的摺疊部分內)粗糙地如此我轉向Control.Lens試圖解開一些混亂。

+1

你有沒有考慮過像ziplist而不是普通列表?這將使得在焦點元素之前或之後插入元素變得很容易。 – kqr

+0

我已經考慮過了,但初始數據結構的構建方式很難轉換爲ziplist,所以我可能必須在事實之後將其轉換,這意味着完全獨立的類型層次結構只是爲了包含ziplists。無論是我還是我從列表轉換成ziplist,然後每次我需要做一個插入,我可以做,但似乎沒有比首先摺疊整個列表更好。 – Orclev

回答

2

我認爲一個簡單的方法是在被打破的問題:

  • 一個功能是[a] -> SomeAddtionalData -> [a],這基本上是負責使用一些特定的數據列表轉換爲另一個列表。這是您添加/刪除列表中的元素並獲取新列表的地方
  • 使用lense從某個嵌套數據結構中提取List,將該列表傳遞給上述定義的函數,使用lense將嵌套數據結構中的返回列表設置爲。

您的最後一段是關於您嘗試使用像Lens這樣的通用抽象進行太多操作時所發生情況的指示。這些泛型抽象對於某些通用目的是有利的,其他一切都是針對你的問題的,並且應該圍繞普通的舊函數進行設計(至少在最初階段,稍後在你的項目中你可能會發現可以使用抽象的代碼庫中的一些通用模式類型類等)。

+0

我想你錯過了最後一段,這就是我沒有使用鏡頭時發生的事情。數據的嵌套特性意味着我正在使用摺疊遍歷多層列表以找到我想要插入新元素的位置。使用鏡頭至少走父母名單似乎更清潔,雖然現在看起來我可能會卡住至少一個褶皺做實際的插入。 – Orclev

2

你的問題的一些意見:

回答這個問題: 可能有辦法做你想做的事情。鏡頭庫是非常通用的。沒有什麼是簡單或明顯的方式來實現它。我認爲這將涉及partsOf combinator,但我不確定。

對鏡頭的評論: 鏡頭庫非常酷,可以應用於很多問題。當我正在學習圖書館時,我最初的誘惑是試圖將所有事情都納入Lens訪問或突變。我發現最好使用鏡頭庫來挖掘我複雜的數據結構,但是一旦我有了一個簡單的元素,最好使用我已經知道的更傳統的功能技術,而不是將鏡頭庫擴展過去,限制。

你沒有要求的建議: 將一個元素插入列表中間是一個壞主意。並不是說它不能完成,但最終可能是O(n^2)操作。 (See also this StackOverflow answer.)郵編列表或其他functional data structure可能是一個更好的主意。作爲一個副作用,這些結構中的一些可以作爲At類的實例,允許使用部分透鏡組合器來插入和刪除。

4

使用lens pacakge

如果我們開始知道功能id可以像一個鏡頭中:

import Control.Lens 
> [1,2,3,4] ^. id 
[1,2,3,4] 

然後我們就可以移動到列表如何可以修改:

> [1,2,3,4] & id %~ (99:) 
[99,1,2,3,4] 

以上允許插入列表的開始。要關注清單的後面部分,我們可以使用Control.Lens.Cons module中的_tail

> [1,2,3,4] ^. _tail 
[2,3,4] 
> [1,2,3,4] & _tail %~ (99:) 
[1,99,2,3,4] 

我們概括本作的第n個位置

> :{ 
let 
_drop 0 = id 
_drop n = _tail . _drop (n - 1) 
:} 
> [1,2,3,4] ^. _drop 1 
[2,3,4] 
> [1,2,3,4] & _drop 0 %~ (99:) 
[99,1,2,3,4] 
> [1,2,3,4] & _drop 1 %~ (99:) 
[1,99,2,3,4] 

最後一步在所有類型有Cons instance我們可以使用cons<|來概括這一點。

> [1,2,3,4] & _drop 1 %~ (99<|) 
[1,99,2,3,4] 
> import Data.Text 
> :set -XOverloadedStrings 
> ("h there"::Text) & _drop 1 %~ ('i'<|) 
"hi there"