【问题标题】:Replace all elements of a List替换列表的所有元素
【发布时间】:2021-01-13 09:09:52
【问题描述】:

我必须用该元素的出现次数替换列表中的所有元素,比如如果我有“Taylor Swift”,结果将是 [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]

但是这些都不起作用... 谁能帮帮我?

【问题讨论】:

  • 当您说“这些都不起作用”时,请详细说明问题所在。你得到编译器拒绝吗?那么请给出错误信息。它是否编译并运行但返回错误的答案?然后举一个小例子说明它返回什么以及为什么你说它是错误的。
  • 您似乎在首次发布后对其进行了编辑,以删除预期输出的示例。 O.P. 是否还包含ocurr 的定义?就现在的 q 而言,它是无用的,威廉的回答没有意义。

标签: list haskell replace


【解决方案1】:

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

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'

【讨论】:

  • 是的,嗯。这实际上是一个思考最优解的非常有趣的问题。我想知道“删除 [ing] 不再需要的键”(从而构建中间列表)实际上是否会比典型数据提供更差的性能?如果输入列表是某种语言的文本,则分布很可能遵循 Zipf 定律。然后会有一个长尾的字符出现一次。 (确实在 O.P. 每个 Char 只出现一次。最好尝试使用旧的备用“Mississippi”。)他们只需要计数一次。连续重建列表以删除元素可能不值得。
  • @AntC,不知道你说的重建列表是什么意思。
  • 啊,好的,您的意思是从IntMap 中“删除”,其中“删除”表示设置为Nothing。我不确定(考虑到我在猜测输入列表的模式)IntMap 是否值得“重工程”。是否值得在 Nothingising 之后为仅出现一次的字符构建?比较我的第二种方法,我通过忽略单例来保持缓存紧凑。
  • @AntC,我没有为Nothing 设置任何内容。在这种情况下,Nothing 从映射中删除键。我不明白你的代码,或者如何有效地忽略单例。
【解决方案2】:

为了与@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
    

【讨论】:

  • 回答@dfeuer对他的回答的q/comment:数据类型Cache3结合了缓存和输入列表;构造函数Cached3 将缓存条目放在列表的前面。但只有在计算该元素的第一次出现(cached 中的go 为假)并且计数大于1c &lt; 2 为假)之后才会出现缺点。与插入IntMap's 或Set's BTree 相比,将缓存作为 cons-list 保存是“低效”(可以说是搜索),而 cons 操作“便宜”。
【解决方案3】:

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

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

你也可以使用简单的map :: (a -&gt; b) -&gt; [a] -&gt; [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]

【讨论】:

    猜你喜欢
    • 2013-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-18
    相关资源
    最近更新 更多