[英]Positional equality in haskell list
如果我有兩個列表,則想定義元素之間的位置相等(在特定意義上)。 例如,如果:
k = [[3,1,2,4],[1,4,2,3],[1,3,4,2]]
s = [["a","b","c","d"],["d","a","c","b"],["c","b","a","d"],["d","b","c","a"]]
我想對函數說2 ∼ "c"
,並返回2
和c
在列表中具有相同位置的所有元組。
res= [([3,1,2,4],["a","b","c","d"])
,([3,1,2,4],["d","a","c","b"])
,([3,1,2,4],["d","b","c","a"])
,([1,4,2,3],["a","b","c","d"])
,([1,4,2,3],["d","a","c","b"])
,([1,4,2,3],["d","b","c","a"])
]
這樣的事情在某種其他語言中將是兩個循環的問題,但是我花了一天的大部分時間試圖用Haskell編寫此函數。 我目前的嘗試:
eqElem i1 (l1:ls1) i2 (l2:ls2) = helper1 i1 l1 i2 l2 0 where
helper1 i1 (p:ps) i2 l2 ctr1
| i1 == p = helper2 i2 l2 ctr1 0
| otherwise = helper1 i1 ps i2 l2 (ctr1+1)
helper2 i2 (p:ps) ctr1 ctr2
| i2==p && ctr1==ctr2 = (l1,l2):(eqElem i1 (l1:ls1) i2 ls2)
| otherwise = helper2 i2 ps ctr1 (ctr2+1)
helper2 i2 [] ctr1 ctr2 = eqElem i1 ls1 i2 (l2:ls2)
eqElem i1 [] i2 _ = []
現在,這給出了:
*Main Lib> eqElem 2 k "c" s
[([3,1,2,4],["a","b","c","d"]),([3,1,2,4],["d","a","c","b"])]
只有大約一半的權利; 如果我堅持下去,我可能會做對,但我只是想確保我沒有重新發明輪子或其他東西。
那么... Haskell的慣用方式是什么? 有一個嗎? 我覺得我在強迫Haskell成為當務之急,必須有一些可以用於完成此任務的高階函數或方法。
最大的問題是我不知道這些清單。 它們可以是任意數據類型,不同的長度和/或(嵌套的)深度。
它們從用戶輸入解析為REPL,並存儲在ADT中,而ADT最多可以制成Functor
, Monad
和Applicative
。 列表理解將需要Alternative
和MonadPlus
但不能做到這些,因為類別理論會發瘋。
可能這樣的事情很慣用(如果不是超級有效的話):
import Data.List
eqElem l r lss rss =
[ (ls, rs)
| ls <- lss
, rs <- rss
, findIndex (l==) ls == findIndex (r==) rs
]
在ghci中:
> mapM_ print $ eqElem 2 "c" [[3,1,2,4],[1,4,2,3],[1,3,4,2]] [["a","b","c","d"],["d","a","c","b"],["c","b","a","d"],["d","b","c","a"]]
([3,1,2,4],["a","b","c","d"])
([3,1,2,4],["d","a","c","b"])
([3,1,2,4],["d","b","c","a"])
([1,4,2,3],["a","b","c","d"])
([1,4,2,3],["d","a","c","b"])
([1,4,2,3],["d","b","c","a"])
這有兩個效率問題:1.重復地重新計算輸入元素在輸入列表中的位置,並且2.遍歷所有對輸入列表。 因此,此方法為O(mnp),其中m是lss
的長度,n是rss
的長度,p是lss
或rss
的最長元素的長度。 效率更高的版本(每個輸入列表僅調用一次findIndex
,並迭代更少的列表對; O(mn + mp + np + m log(m)+ n log(n))看起來像這樣:
import Control.Applicative
import qualified Data.Map as M
eqElem l r lss rss
= concat . M.elems
$ M.intersectionWith (liftA2 (,)) (index l lss) (index r rss)
where
index v vss = M.fromListWith (++) [(findIndex (v==) vs, [vs]) | vs <- vss]
基本思想是構建Map
,該Map
告訴哪些輸入列表在哪個位置具有給定的元素。 然后,這兩個地圖的交點將在給定元素在相同位置的輸入列表liftA2 (,)
,因此我們可以使用liftA2 (,)
來獲取值的笛卡爾積。
再次在ghci中:
> mapM_ print $ eqElem 2 "c" [[3,1,2,4],[1,4,2,3],[1,3,4,2]] [["a","b","c","d"],["d","a","c","b"],["c","b","a","d"],["d","b","c","a"]]
([1,4,2,3],["d","b","c","a"])
([1,4,2,3],["d","a","c","b"])
([1,4,2,3],["a","b","c","d"])
([3,1,2,4],["d","b","c","a"])
([3,1,2,4],["d","a","c","b"])
([3,1,2,4],["a","b","c","d"])
這樣的事情可能是其他語言中的兩個循環的問題
然后,列表理解實際上很簡單:
eqElem a b ass bss =
[ (as,bs) | as <- ass, bs <- bss, any (==(a,b)) $ zip as bs ]
讀取:對於來自ass
每個子列表as
以及(嵌套)每個bss
子列表bs
,檢查as
和bs
何時zip
在一起,是否any
等於(a,b)
元組,然后將(as,bs)
到結果中。
當子列表包含重復的元素時,這應該可以解決這種情況。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.