如何反转字符串中的字符,保留原始索引处的数字?

时间:2016-10-13 07:42:48

标签: haskell

我最近开始学习Haskell。我试图反转包含字符和数字的字符串。该字符串将被反转,保持原始位置的数字。

  

Original String =“H1AW2J1”

     

反转= J1WA2H1

我试图遵循我在Swift中使用的模式。

  1. 过滤数字,然后反转字符串。
  2. 找到原始字符串中的数字索引。
  3. 将从步骤2获得的索引处的数字插入到步骤1的字符串中。
  4. 以下是按照上述步骤操作的代码。

    reversChars“H1AW2J1”重新调整“JWAH”

    reversedChars  = reverse . filter isLetter
    

    索引返回元组。

    indexes xs = [(x, elemIndex x xs) | x <- xs]
    
      

    [('H',Just 0),('1',Just 1),('A',Just 2),('W',Just 3),('2',Just 4),( 'J',Just 5),('1',Just 1)]

    问题

    1. 如何将相应索引处的数字插入reversedChars
    2. elemIndex返回相同的索引Just 1,多次出现1。

3 个答案:

答案 0 :(得分:5)

  
      
  1. 如何将相应索引处的数字插入reversedChars
  2.   

我会使用这样的递归函数,如果它遇到一个字母,会插入下一个反向字符,否则只插入实际字符:

import Data.Char

reverseLetters :: String -> String
reverseLetters xs = go xs (reverse $ filter isLetter $ xs)
  where
    go xs [] = xs
    go (x:xs) (r:rs) | isLetter x = r : go xs rs
                     | otherwise  = x : go xs (r:rs)

main = print $ reverseLetters "H1AW2J1"

输出:

"J1WA2H1"
  
      
  1. elemIndex返回相同的索引Just 1,多次出现1。
  2.   

那是因为这是elemIndex定义返回的内容。如果你想要所有指数,你可以这样做:

Prelude> map fst $ filter ((== '1') . snd) $ zip [0..] "H1AW2J1"
[1,6]

答案 1 :(得分:2)

我的解决方案看起来也类似于@ Dogbert,但是这个解决问题的方式实现了它。

标准库中的列表没有update函数,即使有,也不是一个好主意,因为它会有O(n),其中n是索引,因为列表是单链表。相反,你必须编写自己的函数来将数字包含在字符串中:

reverseChars :: [Char] -> [Char]
reverseChars cs = insert reversedChars digits
  where reversedChars = reverse $ filter isLetter cs
        digits = filter (\(_,c) -> isDigit c) $ indexes cs

insert :: [a] -> [(Int, a)] -> [a]
insert = go 0
  where go i (x:xs) ((j,y):ys)
          | j > i = x : go (i+1) xs ((j,y):ys)
          | otherwise = y : go (i+1) (x:xs) ys
        go _ xs [] = xs
        go _ [] ys = map snd ys

indexes :: [a] -> [(Int, a)]
indexes = zip [0..]

我也改变了索引的实现,因为elemIndex也有复杂度O(n),n是列表中搜索元素的位置,所以你的索引函数会有O(n²) 。此外,通过此实现,您可以摆脱Maybe中的indexes

答案 2 :(得分:0)

不同的方法不需要找到单个元素的索引:

reverseIf :: (a -> Bool) -> [a] -> [a]
reverseIf f = fill <*> reverse . filter f
  where fill s []     = s
        fill (s:ss) (r:rs)
          | f s       = x : fill ss rs
          | otherwise = s : fill ss (r:rs)

这是另一个版本:

reverseIf :: (a -> Bool) -> [a] -> [a]
reverseIf f = snd . (flip (mapAccumL g) <*> reverse . filter f)
  where g r e | f e       = (tail r, head r)
              | otherwise = (r, e)

调用:

> reverseIf (not . isDigit) "H1AW2J1"
"J1WA2H1"