簡體   English   中英

Haskell:使用元素和列表連接元組列表:[(a,[b])] - > [(a,b)]

[英]Haskell: concat a list of tuples with an element and a list: [(a,[b])] -> [(a,b)]

我想在元素和字符串的元組列表中連接,每個字符都包含元素。

例如:[(True,“xy”),(False,“abc”)]􏰁-> [(True,'x'),(True,'y'),(False,'a'),(False , 'b'),(假, 'C')]

我確實有一個解決方案,但我想知道是否有更好的解決方案:

concatsplit :: [(a,[b])] -> [(a,b)]
concatsplit a = concatMap (\(x,y)-> concatsplit' (x,y)) a

concatsplit' :: (a,[b]) -> [(a,b)]
concatsplit' y = map (\x -> ((fst y),x)) (snd y)

為什么不簡單列表理解?

列表推導通常可以與更高階的函數一樣多,我認為如果你不需要更改數據但只需“解包”它,它們就非常清楚了。 這是一個有效的例子:

concS :: [(a,[b])] -> [(a,b)]
concS ls = [(a,b) | (a,x) <- ls, b <- x]

sequenceA讓它變得非常簡單:

concatsplit = concatMap sequenceA

或者進一步概括:

concatsplit = (>>= sequenceA)

細節:

  • sequenceA具有類型(Applicative f, Traversable t) => t (fa) -> f (ta) 這意味着如果你在“外部”上有一個Traversable類型,在“inside”上有一個Applicative ,你可以調用它上面的sequenceA來將它從里面翻出來,這樣Applicative就在“outside”上面了Traversable在“內部”。
  • 在這種情況下, (True, "xy")具有類型(Bool, [Char]) ,它取消了(,) Bool ([] Char) ((,) a)一個 Traversable 實例 ,並且[]一個 Applicative 實例 因此,你可以在它上面調用sequenceA ,結果將是[] ((,) Bool Char) ,或者帶糖的[(Bool, Char)]
  • 事實證明, sequenceA不僅有一個有用的類型,而且它完全符合你需要的東西(事實證明它完全等同於concatsplit' )。
  • concatMap類型為Foldable t => (a -> [b]) -> ta -> [b] (>>=)類型Monad m => ma -> (a -> mb) -> mb 當專門用於列表時,它們變得相同,除了它們的參數順序相反。

其他答案顯示了如何從頭開始慣用這個,我非常喜歡。 展示你如何改進你已經擁有的東西可能也很有趣。 這里再次提醒一下:

concatsplit a = concatMap (\(x,y)-> concatsplit' (x,y)) a
concatsplit' y = map (\x -> ((fst y),x)) (snd y)

我會一直改變的第一件事被稱為“埃塔減少”,這是當你把東西形狀的\\x -> foo x逼到foo 我們可以在concatMap的參數中做到這一點

concatsplit a = concatMap concatsplit' a

然后在concatsplit的爭論中再次獲得:

concatsplit = concatMap concatsplit'

看看concatsplit' ,我最不喜歡的是使用fstsnd而不是模式匹配。 使用模式匹配,它看起來像這樣:

concatsplit' (a,bs) = map (\x -> (a,x)) bs

如果你真的想練習你的eta減少,你可能會注意到(,)可以應用前綴並將其更改為

concatsplit' (a,bs) = map (\x -> (,) a x) bs
                    = map ((,) a) bs

但我覺得我和其他人一樣快樂。 在這一點上,這個定義足夠小,我很想把它內聯到concatsplit本身,得到:

concatsplit = concatMap (\(a,bs) -> map ((,) a) bs)

這看起來對我來說是一個非常好的定義,我會在那里停下來。

可能會被這里幾乎減少的東西所困擾:它幾乎是放棄bs的正確形狀。 高級用戶可能會繼續,注意到:

uncurry (\a bs -> map ((,) a) bs) = \(a,bs) -> map ((,) a) bs

因此,通過一些eta減少和其他無點技術,我們可以通過這種方式進行轉換:

concatsplit = concatMap (uncurry (\a bs -> map ((,) a) bs))
            = concatMap (uncurry (\a -> map ((,) a)))
            = concatMap (uncurry (map . (,)))

但是,就個人而言,我發現這比我聲明我會停在上面的地方更不易讀,即:

concatsplit = concatMap (\(a,bs) -> map ((,) a) bs)

您還可以將monad實例用於列表:

concatSplit l = l >>= \(a,x) -> x >>= \b -> return (a,b)

哪個可以簡化為:

concatSplit l = l >>= \(a,x) -> map ((,) a) x

並重新格式化,以do記號

concatSplit l = do
  (a,x) <- l
  map ((,) a) x

暫無
暫無

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

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