【问题标题】:mapEither inserting both Left and RightmapEither 插入 Left 和 Right
【发布时间】:2022-01-17 03:31:49
【问题描述】:

将函数mapEither 用于多重集,我可以将MultiSet 变成一对两个多重集。当f 返回Left 时,该元素被插入到该对的第一个Multiset,如果f 返回Right,则该元素被插入到该对的第二个MultiSet

如何将相同的元素同时插入两个MultiSets,就像f 同时返回RightLeft

f:: LocalType -> Either LocalType LocalType
f (Sometype lt) = Left lt -- And Right lt
f lt = Left lt

parRule :: (MultiSet LocalType) -> (MultiSet LocalType)
parRule sequent = do 
    let list = MultiSet.mapEither f sequent

作为参考,我使用 Data.Multiset 包,https://hackage.haskell.org/package/multiset-0.3.4.3/docs/Data-MultiSet.html

【问题讨论】:

  • 只需将 the source 调整为 dfeuer says
  • @WillNess,我不太喜欢它的实现方式。
  • @dfeuer 是的,我明白了,所以我链接到您的评论——我认为它建议在我们进行时构建两个多重集,而不是将这些对添加到两个列表中。我还没有看到你的答案。

标签: haskell multiset


【解决方案1】:

您可以使用These 之类的类型来捕获返回两者的能力。然后您可以使用toAscOccurListfromOccurList(或fromAscOccurList,如果您的函数是单调的)来计算新的MultiSet

【讨论】:

  • 这是包使用的策略,但它让我感到相当惊讶。为什么不在划分结果的同时急切地构建新的多重集呢?它闻起来过度懒惰。
【解决方案2】:

您可以按照 Daniel Wagner 的建议使用 These,但我会使用稍微不同的函数开始,这似乎更适合库 API。此外,我会推荐一种不同的性能实施策略。

data SP a b = SP !a !b
toPair :: SP a b -> (a, b)
toPair (SP a b) = (a, b)

mapPairOcc :: (Ord b, Ord c) => (a -> Occur -> ((b, Occur), (c, Occur))) -> MultiSet a -> (MultiSet b, MultiSet c)
mapPairOcc f = toPair . mapPairOcc' f

mapPairOcc' :: (Ord b, Ord c) => (a -> Occur -> ((b, Occur), (c, Occur))) -> MultiSet a -> SP (MultiSet b) (MultiSet c)
mapPairOcc' f = foldl' go (SP empty empty) . toAscOccurList
  where
    go (SP bs cs) a
      | ((b, bn), (c, cn)) <- f a
      = SP (insertMany b bn bs) (insertMany c cn cs)

当您知道f 在某种意义上是严格单调时

a < a' ==> fst (f a) < fst (f a') /\ snd (f a) < snd (f a')

有可能做得更好,在 O(n) 时间内构建结果。最好的方法似乎是使用Data.Map internals。我将重用上面的 SP 类型。

import Data.Map.Lazy (Map)
import Data.MultiSet (MultiSet, Occur)
import qualified Data.MultiSet as MS
import qualified Data.Map.Internal as M
import Control.Monad (guard)

-- | Map over the keys and values in a map, producing
-- two maps with new keys and values. The passed function
-- must be strictly monotone in the keys in the sense
-- described above.
mapMaybeWithKey2Mono :: (k -> a -> (Maybe (l,b), Maybe (m,c))) -> Map k a -> (Map l b, Map m c)
mapMaybeWithKey2Mono f = toPair . mapMaybeWithKey2Mono' f

mapMaybeWithKey2Mono' :: (k -> a -> (Maybe (l,b), Maybe (m,c))) -> Map k a -> SP (Map l b) (Map m c)
mapMaybeWithKey2Mono' _ M.Tip = SP M.Tip M.Tip
mapMaybeWithKey2Mono' f (M.Bin _ kx x l r)
  | (fl, fr) <- f kx x
  = SP (groink fl mfl1 mfr1) (groink fr mfl2 mfr2)
  where
    groink :: Maybe (q, x) -> Map q x -> Map q x -> Map q x
    groink m n o = case m of
      Just (k', y) -> M.link k' y n o
      Nothing -> M.link2 n o
    SP mfl1 mfl2 = mapMaybeWithKey2Mono' f l
    SP mfr1 mfr2 = mapMaybeWithKey2Mono' f r

使用这个新的通用Map 函数,我们可以在多重集上定义我们想要的函数:

mapPairAscOcc :: (a -> Occur -> ((b, Occur), (c, Occur))) -> MultiSet a -> (MultiSet b, MultiSet c)
mapPairAscOcc f m
  | (p, q) <- mapMaybeWithKey2Mono go . MS.toMap $ m
  = (MS.fromOccurMap p, MS.fromOccurMap q)
  where
     -- a -> Occur -> (Maybe (b, Occur), Maybe (c, Occur))
    go a aocc
      | ((b, bocc), (c, cocc)) <- f a aocc
      = ( (b, bocc) <$ guard (bocc > 0)
        , (c, cocc) <$ guard (cocc > 0) )

【讨论】:

  • strict 包中还提供了严格的配对类型,即Data.Strict.Tuple.Pair。或者,在这种情况下,go 可以在(惰性)元组上使用严格模式匹配而不是严格构造,例如,使用BangPatterns,将模式SP bs cs 替换为(!bs, !cs)
  • @JonPurdy,我的运气更好,无论是在人类方面还是在编译器方面,使用严格对而不是到处使用爆炸模式。诚然,我比较它们的经验主要是在多态递归的上下文中,与更典型的情况相比,GHC 的优化器往往需要更多的手动操作。很高兴了解 strict 包,但在实践中,我经常需要比任何库可能提供的更具体(通常是部分)严格的类型(例如data HSPN a b = HSPN !Int !a b)。
  • 我想当f 是单调的时,人们也可能希望有一个 O(n) 解决方案。
  • @DanielWagner,我试图变得非常聪明的尝试失败了。我给你一个单调版本的实现,它使用Data.Map 内部实现了巨大的正义。
【解决方案3】:

我从Data.MultiSet 中取出函数mapEither 并对其进行了修改,使其支持These 类型。

-- | /O(n)/. Map and separate the 'This' and 'That' or 'These' results 
-- modified function of mapEither to map both cases in case f return These
-- code of mapEither found in source code, 
mapThese :: (Ord b, Ord c) => (a -> These b c) -> MultiSet a -> (MultiSet b, MultiSet c)
mapThese f = (\(ls,rs) -> (MultiSet.fromOccurList ls, MultiSet.fromOccurList rs)) . mapThese' . MultiSet.toOccurList
  where mapThese' [] = ([],[])
        mapThese' ((x,n):xs) = case f x of
           This  l -> let (ls,rs) = mapThese' xs in ((l,n):ls, rs)
           That r -> let (ls,rs) = mapThese' xs in (ls, (r,n):rs)
           These u i -> let (ls,rs) = mapThese' xs in ((u,n):ls, (i,n):rs)

在 f 返回 These 的情况下,两个 MultiSet 都有一个添加元素。

【讨论】:

  • mapThese' 所做的大部分工作都被partitionHereThere 捕获。所以我可能会写mapThese f m = bimap MS.fromOccurList MS.fromOccurList $ partitionHereThere [bimap (,n) (,n) (f x) | (x, n) &lt;- MS.toOccurList m] 或类似的。
猜你喜欢
  • 1970-01-01
  • 2011-03-12
  • 2020-01-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-27
  • 2017-03-25
相关资源
最近更新 更多