我有一個帶有兩個資料結構的程式,我希望將其結合起來。 在這里使用Data.Map是偶然的,因為我在其他地方使用它來達到一個相關目的。 如果一個解決方案從未使用過Data.Map,那也可以(可能更好)。我已經將問題簡化為下面的腳本,其中包含了所有的基本元素。
我的實際程式是在一個不同的領域,但是在這個類比中,各種 "采訪者 "被分配去采訪給定家庭中的所有人員(由 "房子 "的索引位置命名)。 我想確定哪些采訪者將需要進行多次采訪。
如果一個采訪者被分配到多個家庭,她就自動必須采訪多個人(在情景中,所有家庭都有人)。 然而,如果她只被分配到一個家庭,她可能還需要采訪那里的幾個人。
我最初發現的錯誤方法(被我對領域的錯誤假設所誤導)產生了下面的結果。 然而,我在制定正確的解決方案時遇到了困難。對于我的目的來說,訪談在結果中出現的順序并不重要。
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
--從對的串列中創建Map,使dup vals成為一個串列。
fromListWithDuplicates :: Ord k => [(k, v)] -> Map k [v
fromListWithDuplicates 對 =
Map.fromListWith ( ) [(k, [v]) | (k, v) <- pairs] 。
data Person = Person {
name :: String :: String
} deriving (Show, Eq)
households = [[Person "Alice", Person "Bob"]。
[Person "Carlos"]。
[Person "Dabir", Person "Eashan"] 。
[Person "Fatima"] 。]
interviewers = [("Agent1", [0]), ("Agent2", [1,2]), ("Agent3", [3]) ]
multiInterviewsWRONG households interviewers =
let assignments = [(agent, name person)|
(agent, houseIndices) <- interviewers,
index <- houseIndices,
person <- (houses !!index),
length houseIndices > 1 ]
in Map.assocs $ fromListWithDuplicates assignments
main :: IO ()
main = do
--列印。[("Agent2", ["Eashan", "Dabir", "Carlos"])]/span>
putStrLn $ show (multiInterviewsWRONG households interviewers)
-- 正確:[("Agent2", ["Eashan", "Dabir", "Carlos"]),
--("Agent1", ["Alice", "Bob"]]
后續:這個解決方案只是Willem Van Onsem的下文,但把它放在一個地方:
import Util (lengthExceeds)
multiInterviews households interviewers =
let assignments = [(agent, name person)|
(agent, houseIndices) <- interviewers,
index <- houseIndices,
person <- (houses !!index) ] 。
in filter (flip lengthExceeds 1 . snd)
(Map.assocs $ fromListWithDuplicates assignments)
uj5u.com熱心網友回復:
顯然Willem的答案很好,但我認為也可以提供一個沒有串列理解的答案:
atLeastTwo :: [a] -> Bool[/span
atLeastTwo (_:_:_) =True
atLeastTwo _ = False
transformSnd :: (b -> c) -> (a, b) -> (a, c)
transformSnd fun (f, s) = (f, fun s)
--或者transformSnd = second (from Control.Arrow; h/t Willem)
-- or transformSnd = fmap (from (,)'s Functor instance; h/t Will Ness)
-- or transformSnd = second (from Data.Bifunctor)
mult :: [(String, [String])
mult = filter (atLeastTwo . snd) . map (transformSnd toInterviewees) $ interviewers
where toInterviewees = map name . concatMap (holds !!)
--mult == [("Agent1",["Alice", "Bob"]),("Agent2",["Carlos", "Dabir", "Eashan"]]
我合理地確信這兩個版本的運行速度相同;哪個版本更具有可讀性,取決于誰在進行閱讀。
有幾個功能上的差異。首先,在Willem的答案中,你得到的是一張地圖,而在這個答案中,你得到的是一個串列(但這種差異主要是外觀上的,而你說你并不關心)。
其次,如果interviewers串列中有兩對具有相同的第一個元素,那么這兩個版本的行為是不同的。按照 Willem 的方法,你可能會做你想做的事情,也就是說,把他們當作一個具有較長第二元素的一對;按照這個方法,你會在結果串列中得到兩個具有相同第一元素的對。
另外,你可能知道這一點,但是:如果你發現自己經常合并串列,你可能需要用集合來代替。
uj5u.com熱心網友回復:
你應該洗掉length houseIndices > 1約束,因為這意味著它將只保留代理人,因為他們必須采訪兩個或更多的家庭。因此,你應該使用作為串列的理解力:
multiInterviews households interviewers =
let assignments = [
(agent, name person) |
(agent, houseIndices) <- interviewers,
index <- houseIndices,
人 <- 住戶 !" 索引
]
# ...
給定的串列理解將產生一個看起來像:
的串列。Prelude> :{
Prelude| [
Prelude| (agent, name person) | Prelude|
Prelude| (agent, houseIndices) <- interviewers,
Prelude| index <- houseIndices。
Prelude| person <- households !! index
Prelude| ].
Prelude| :}。
[("Agent1","Alice"), ("Agent1", "Bob"),("Agent2","Carlos") 。 ("Agent2","Dabir"), ("Agent2", "Eashan"),("Agent3","Fatima") ]
然而我們需要進行過濾,我們可以用至少包含兩個專案的串列來查看assocs。我們可以實作一個高效的函式來確定串列是否至少有兩個專案:
atLeastTwo :: [a] -> Bool[/span
atLeastTwo (_:_:_) =True
atLeastTwo _ = False
并對Map的assocs應用這個過濾器:
multiInterviews households interviewers =
let assignments = ...
in filter (atLeastTwo . snd) (Map.assocs (fromListWithDuplicates assignments))/code
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/316925.html
標籤:
