簡體   English   中英

Haskell列表中的位置相等

[英]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" ,並返回2c在列表中具有相同位置的所有元組。

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最多可以制成FunctorMonadApplicative 列表理解將需要AlternativeMonadPlus但不能做到這些,因為類別理論會發瘋。

可能這樣的事情很慣用(如果不是超級有效的話):

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是lssrss的最長元素的長度。 效率更高的版本(每個輸入列表僅調用一次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 ,檢查asbs何時zip在一起,是否any等於(a,b)元組,然后將(as,bs)到結果中。

當子列表包含重復的元素時,這應該可以解決這種情況。

暫無
暫無

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

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