【问题标题】:Partition list based on data constructor基于数据构造函数的分区列表
【发布时间】:2018-10-12 23:04:21
【问题描述】:

假设我有以下内容:

data D = A Int | B Int deriving Show

我有一个函数

simplify :: [D] -> [D]

我的目标是简化创建一个新列表,它将所有值与数据 A 相加(到数据 A 的单个值中)并保持 B 数据不变。

例如,[A 1, A 2, A 3, B 1, A 4, B 2] 将变为 [A 10, B 1, B 2]

我知道我可以用 foldl 做到这一点:

A (foldl (+) 0 [x | A x <- ll]) : [B x | B x <- ll]

但这涉及到遍历列表两次寻找构造函数。 我想知道是否有一种使用分区的方法,我可以将列表分成有数据A 和没有数据的列表。

【问题讨论】:

  • 如果你对列表进行分区,然后遍历分区,你仍然会遍历列表两次。
  • 如果我运行分区并获取列表a和b,我不能折叠a并与b连接吗?我不必再次将 B 数据与列表进行模式匹配

标签: haskell partition


【解决方案1】:

如果您可以始终将 A 值放在 Bs 之前,这似乎是可行的。

simplify :: [D] -> [D]
simplify = uncurry (:) . foldr f (A 0, [])
  where
  f (A x) ((A n), acc) = (A (n+x),   acc)
  f b     (a    , acc) = (a      , b:acc)

虽然老实说我认为uncurry (:) 在这里是一个错误,但您的最终类型应该是:

simplify :: [D] -> (D, [D])

【讨论】:

  • 我接受了你的,因为它更符合我的目标。我还从阅读其他示例中注意到的是带有分区的模式匹配函数的用例。这有助于更复杂的“简化”,我可能需要为多种数据类型调用操作
  • 也许你想将你的第二个参数惰性模式化为f
【解决方案2】:

你不需要分区,只需要一个左折叠:

import Data.List (mapAccumL)

simplify :: [D] -> [D]
simplify = f . mapAccumL g 0 
  where
  g acc (A i) = acc `seq` (acc+i, [])
  g acc b     = (acc,  [b])
  f (acc, ys) = A acc : concat ys

【讨论】:

  • 关于mapAccumL!与带有Just (B x)Nothing 的累加器相比,您更喜欢这个累加器,然后让fA acc : catMaybes ys? (f 然后变成uncurry (:) . A *** catMaybes
  • acc `seq` acc+i 似乎毫无意义——你确定不想改为acc `seq` (acc+i, []) 吗?
  • @AdamSmith 是的,期望catMaybesconcat 更有效率是合乎逻辑的。列表更加通用,尽管在这种情况下不需要。
猜你喜欢
  • 1970-01-01
  • 2018-09-07
  • 2020-09-07
  • 2011-01-11
  • 2019-06-07
  • 1970-01-01
  • 2020-02-03
  • 1970-01-01
  • 2017-12-15
相关资源
最近更新 更多