替换列表的所有元素

时间:2020-09-27 17:01:00

标签: list haskell replace

我必须用该元素的出现次数替换列表中的所有元素,例如如果我使用“泰勒·斯威夫特”,结果将为[1,1,1,1,1,1,1,1, 1,1,1,1]。

我已经制作了代码来计算出现次数,我只知道如何用我已经尝试过的出现次数替换所有元素:

ocurr :: [Char] -> Char -> Int
ocurr xs x = length(filter (x==) xs)

frequencias :: [Char] -> [Char] 
frequencias "" = []
frequencias xs = [ ocurr xs y| y <- xs]

ocurr :: [Char] -> Char -> Int
ocurr xs x = length(filter (x==) xs)

frequencias :: [Char] -> [Char] 
frequencias "" = []
frequencias xs = [x | y <- xs x = ocurr xs x]

但是这些都不起作用... 有人可以帮我吗?

3 个答案:

答案 0 :(得分:5)

这不起作用,因为您在frequencias中指定的返回类型为[Char],而根据您的occurr函数,频率为Int。空列表的特殊子句不是必需的(尽管没有错)。因此,您可以使用:

frequencias :: [Char] -> [Int]
frequencias xs = [ ocurr xs y | y <- xs ]

您还可以使用简单的map :: (a -> b) -> [a] -> [b]

frequencias :: [Char] -> [Int]
frequencias xs = map (ocurr xs) xs

因此,这给了我们

Prelude> frequencias "Taylor Swift"
[1,1,1,1,1,1,1,1,1,1,1,1]
Prelude> frequencias "taylor swift"
[2,1,1,1,1,1,1,1,1,1,1,2]

答案 1 :(得分:2)

所有这些过滤可能会变得昂贵。这是一个简单的解决方法:

import qualified Data.IntMap.Strict as M
import Data.IntMap.Strict (IntMap)
import Data.Char (ord)
import Control.DeepSeq (force)
import Data.List (foldl')

frequencias :: [Char] -> [Int]
frequencias xs = force res
  where
   freq_map :: IntMap Int
   freq_map = foldl' go M.empty xs
   go fm c = M.insertWith (+) (ord c) 1 fm
   res = map (\c -> case M.lookup (ord c) freq_map of
          Just freq -> freq
          Nothing -> error "impossible") xs

force确保可以立即对频率图进行垃圾回收;如果结果被立即消耗掉,这是不必要的,或者可能是不可取的。

防止内存泄漏的另一种方法是删除不再需要的密钥:

import qualified Data.IntMap.Strict as M
import Data.IntMap.Strict (IntMap)
import Data.Char (ord)
import Data.List (foldl')

data Blob = Blob
  { total_count :: !Int
  , remaining :: !Int
  }

frequencias :: [Char] -> [Int]
frequencias xs0 = finish xs0 freq_map0
  where
    freq_map0 :: IntMap Blob
    freq_map0 = foldl' go M.empty xs0
    go fm c = M.alter f (ord c) fm
      where
        f Nothing = Just (Blob 1 1)
        f (Just (Blob x _)) = Just (Blob (x + 1) (x + 1))
    finish [] _ = []
    finish (c : cs) freq_map = case M.updateLookupWithKey (\_ (Blob tot remn) ->
      if remn == 1
      then Nothing
      else Just (Blob tot (remn - 1))) (ord c) freq_map of
        (Nothing, _) -> error "Whoopsy"
        (Just (Blob tot _), freq_map') -> tot : finish cs freq_map'

答案 2 :(得分:0)

为了与@dfeuer的答案进行比较,这里有一些较低技术的方法。

  1. 暴力法。对于输入列表长度O(n^2),这具有n的时间复杂度。

     occurrences :: Eq a => [a] -> [Int]
     occurrences xss = map (\ x -> count (== x) xss) xss
    
     count :: (a -> Bool) -> [a] -> Int
     count _ [] = 0
     count p (x : xs) | p x       = 1 + count p xs
                      | otherwise =     count p xs
    

(我在功能中使用英文名称;-) count完成了O.P.的ocurr的工作。但是,我已将args的顺序切换为看起来更像Prelude.filterocurr效率不高,因为filter建立了一个中间结果作为length的参数。我们不需要构建它:只需计算有多少元素满足谓词(== x)

(我很惊讶还没有Prelude.countData.List.count。)

这是低效的,因为它遍历了每个元素的列表,即使它已经“知道”该元素值的计数了-即,因为它早已在列表中遇到了该元素。

OTOH,如果大部分元素仅发生一次,则可以避免构建某种查找结构的开销。

  1. 这是使用中间缓存的版本-但仅适用于已知发生多次的元素。任何人都想猜测它的时间复杂度是什么?

     data Cache3 a = TheList3 [a] | Cached3 a Int (Cache3 a)
    
     count3 :: (a -> Bool) -> Cache3 a -> (Int, Bool)
                         -- return both the count and whether it was cached
     count3 p (TheList3 xss) = ( count p xss, False)    -- reuse count from sol'n 1
     count3 p (Cached3 x c xs) | p x = (c, True)
                               | otherwise = count3 p xs
    
     -- don't cache if count = 1: we've just counted its only appearance so won't need it again
    
     occurrences3 :: Eq a => [a] -> [Int]
     occurrences3 xss = go (TheList3 xss) xss  where
       go _ [] = []
       go cc (x: xs) = c: go (if cached || c < 2 then cc else ( Cached3 x c cc)) xs  where
         (c, cached) = count3 (== x) cc