【问题标题】:haskell foldr manipulation with lists带有列表的haskell foldr操作
【发布时间】:2012-10-11 11:39:11
【问题描述】:

给定一个负数和正数序列的列表,我如何使用 foldr 将它们划分为负数和正数序列?

例如 [1,2,3,-1,-2,-3,1,2,3] 我会得到 [[1,2,3],[-1,-2,-3], [1,2,3]]

几个疑问

我如何知道我已经比较的前一个分区是否与我当前比较的分区具有相同的符号?

如何将元素添加到列表中?我尝试了 [x]:y 之类的方法,但我得到的是每个元素作为一个列表并连接在一起,这不是结果。

我目前拥有的是这个

foldr (\ x y -> if  x >= 0  then [x]:y else y ) [[]] 

错了

非常感谢您提前提供的帮助。

【问题讨论】:

    标签: haskell fold


    【解决方案1】:

    我支持groupBy 的用法。但是,我想提出一点,即 0 在数学中不被视为正数。而且到目前为止没有其他答案提到,任何属于Num 类型类的东西都必须实现signum,这将返回给它的数字的符号。

    import Data.List     (groupBy)
    import Data.Function (on) -- Can evade a lambda
    
    signGroup :: (Num a) => [a] -> [[a]]
    signGroup = groupBy ((==) `on` signum)
    

    示例用法:

    > signGroup [1,2,3,0,0,-1,-2,1,2,0,3,4,-1]
    [[1,2,3],[0,0],[-1,-2],[1,2],[0],[3,4],[-1]]
    

    【讨论】:

      【解决方案2】:

      您希望将列表中每个数字的符号与其后继数字的符号进行比较。如果符号相同,则要将x与其后继者放在同一个列表中,否则启动一个新的内部列表。

      所以

      combine x [[]] = [[x]]
      combine x wss@(yys@(y:_):zss)
        | sameSign x y = (x:yys) : zss
        | otherwise    = [x] : wss
      

      会做你想做的(给定sameSign的实现)。但这不会很有效(并且根本不适用于无限列表),因为要知道要使用哪个等式,需要构造 x 之后的部分,这意味着必须首先到达输入列表的末尾,则必须后退一步。

      解决方法是懒惰,你必须在检查第二个参数之前开始构造结果

      combine x wss = (x:ssx) : rest
        where
          (ssx:rest) = case wss of
                         [[]] -> [] : []
                         (yys@(y:ys) : zss)
                             | sameSign x y -> yys : zss
                             | otherwise    -> [] : wss
      

      然后

      foldr combine [[]] input
      

      是你想要的,例如,

      sameSign x y
          | x < 0     = y < 0
          | otherwise = y >= 0
      

      (当然,使用groupBy 更短更容易,但不使用foldr :)

      【讨论】:

        【解决方案3】:

        你需要一个稍微复杂一点的累加器:

        data Sign = Neg | Zero | Pos
        
        signGroup :: [Integer] -> [[Integer]]
        signGroup xs = case
          foldr
          (\x (sign, ps, ns, ys) ->
            -- x    - current element
            -- sign - sign of the prev. group
            -- ps   - positive numbers in the current group
            -- ns   - negative numbers in the current group
            -- ys   - final list
            if x >= 0
            then case sign of
              Neg  -> (Pos, x : ps, [], ns : ys)
              Zero -> (Pos, x : ps, [], ys)
              Pos  -> (Pos, x : ps, [], ys)
            else case sign of
              Neg  -> (Neg, [], x : ns, ys)
              Zero -> (Neg, [], x : ns, ys)
              Pos  -> (Neg, [], x : ns, ps : ys))
          (Zero, [], [], [])
          xs
         of
            (_, [], [], ys) -> ys
            (_, [], ns, ys) -> ns : ys
            (_, ps, [], ys) -> ps : ys
            (_, ps, ns, ys) -> ps : ns : ys -- <- unreachable
        
        -- signGroup [1,2,3,-1,-2,-3,1,2,3]
        -- => [[1,2,3],[-1,-2,-3],[1,2,3]]
        

        【讨论】:

        • @Landei,是的,这太复杂了;) Daniel Fischer 的答案在这里有更好的文件夹功能。
        【解决方案4】:

        使用最适合工作的工具。在这种情况下,最好的工具是groupBy

        groupBy 将列表的两个成员传递给您的函数,因此您可以轻松检查它们是否具有相同的符号。

        (如果你真的想的话,你可以用foldr来写——你可以用foldr来写groupBy,虽然标准实现没有——它只是让事情变得更多对你来说比他们需要的复杂。)

        【讨论】:

          【解决方案5】:

          你可以得到你想要的使用例如groupBy 来自Data.List

          import Data.List (groupBy)
          let result = groupBy (\ x y -> x*y>0) [1,2,3,-1,-2,-3,1,2,3]
          

          如果您不想单独处理 0,请使用例如Daniel Fischer 的 sameSign 函数用于检查。

          【讨论】:

            【解决方案6】:

            我将使用 foldr 提供可能的答案。我并不是说你应该使用它,因为它既不是很有效(我使用 a*b >=0 来表明 a 和 b 具有相同的符号),也不适用于无限列表。

            combine = foldr f []
                where
                    f a [] = [[a]]
                    f a rest@((x:xs):ys) | x*a >= 0 = (a:x:xs):ys
                                         | otherwise = [a]:rest
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-12-21
              • 1970-01-01
              • 2017-02-11
              • 1970-01-01
              相关资源
              最近更新 更多