【问题标题】:Adding zeros between elements in list?在列表中的元素之间添加零?
【发布时间】:2019-10-17 23:54:46
【问题描述】:

我正在尝试更改 haskell 中的列表以在每个元素之间包含 0。如果我们有初始列表[1..20],那么我想将其更改为[1,0,2,0,3..20]

我想做的实际上是在每个函数上使用 map,提取元素然后将其添加到列表并使用 ++[0] 到它,但不确定这是否是正确的方法。还在学习haskell,所以可能会有错误。

我的代码:

x = map classify[1..20] 

classify :: Int -> Int 
addingFunction 0 [Int]


addingFunction :: Int -> [a] -> [a]
addingFunction x xs = [a] ++ x ++ xs 

【问题讨论】:

    标签: list haskell


    【解决方案1】:

    intersperse 就是为此而生的。只需import Data.List (intersperse),然后是intersperse 0 yourList

    【讨论】:

    • 是的,这也有效,但有点想深入了解 Haskell 更多
    【解决方案2】:

    map 无法做到这一点。 map 的基本属性之一是它的输出总是有与其输入一样多的项目,因为每个输出元素对应一个输入,反之亦然。

    不过,有一个具有必要功能的相关工具:

    concatMap :: (a -> [b]) -> [a] -> [b]
    

    这样,每个输入项可以产生零个或多个输出项。您可以使用它来构建您想要的功能:

    between :: a -> [a] -> [a]
    sep `between` xs = drop 1 . concatMap insert $ xs
      where insert x = [sep, x]
    
    0 `between` [1..10]
    [1,0,2,0,3,0,4,0,5,0,6,0,7,0,8,0,9,0,10]
    

    或者更简洁的between定义:

    between sep = drop 1 . concatMap ((sep :) . pure)
    

    【讨论】:

      【解决方案3】:

      简单的模式匹配应该是:

      addingFunction n [] = []
      addingFunction n [x] = [x]
      addingFunction n (x:xs) = x: n : (addingFunction n xs)
      
      addingFunction 0 [1..20]
      => [1,0,2,0,3,0,4,0,5,0,6,0,7,0,8,0,9,0,10,0,11,0,12,0,13,0,14,0,15,0,16,0,17,0,18,0,19,0,20]
      

      【讨论】:

        【解决方案4】:

        如果你想使用map来解决这个问题,你可以这样做:

        有一个函数可以获取一个 int 并返回 2 个带有 int 和零的元素列表:

        addZero :: List
        addZero a = [0, a]
        

        然后就可以用这个函数调用map了:

        x = map addZero [1..20] -- this will return [[0,1], [0, 2] ...] 
        

        您会注意到它是一个嵌套列表。这就是map 的工作方式。我们需要一种方法将内部列表组合成一个列表。本例我们使用foldl

        combineList :: [[Int]] -> [Int]
        combineList list = foldl' (++) [] list 
        -- [] ++ [0, 1] ++ [0, 2] ... 
        

        所以 foldl 在这种情况下的工作方式是它接受一个组合函数、初始值和要组合的列表。

        由于我们不需要第一个 0,我们可以删除它:

        dropFirst :: [Int] -> [Int]
        dropFirst list = case list of
          x:xs -> xs
          [] -> []
        

        最终代码:

        x = dropFirst $ combineList $ map addZero [1..20]
        
        addZero :: Int -> [Int]
        addZero a = [0, a]
        
        combineList :: [[Int]] -> [Int]
        combineList list = foldl (++) [] list 
        
        dropFirst :: [Int] -> [Int]
        dropFirst list = case list of
          x:xs -> xs
          [] -> []
        
        

        【讨论】:

        • foldl (++) [] 有点奇怪。为什么不直接指向concat
        • @amalloy 是的。只是concat 实现本身也使用了某种类型的fold。所以我想使用foldl 会帮助其他人更深入地理解它。
        • concat 是使用foldr 而不是foldl 实现的。你明白为什么这实际上很重要吗?
        • 我不完全理解foldmyself 的主题。有一个与此主题相关的完整维基。我的简单理解是foldr 更适合惰性无限列表,foldlfoldl'(严格版本)更适合一般用例。
        【解决方案5】:

        我们在这里可以使用foldr 模式,对于原始列表中的每个元素,我们在其前面加上0

        addZeros :: Num a => [a] -> [a]
        addZeros [] = []
        addZeros (x:xs) = x : foldr (((0 :) .) . (:)) [] xs
        

        【讨论】:

          【解决方案6】:

          如果你不想使用intersperse,你可以自己写。

          intersperse :: a -> [a] -> [a]
          intersperse p as = drop 1 [x | a <- as, x <- [p, a]]
          

          如果你喜欢,可以使用Applicative操作:

          import Control.Applicative
          
          intersperse :: a -> [a] -> [a]
          intersperse p as = drop 1 $ as <**> [const p, id]
          

          这基本上是Data.Sequence中使用的定义。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2021-06-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-10-03
            • 1970-01-01
            • 1970-01-01
            • 2020-08-19
            相关资源
            最近更新 更多