如何计算每个字符串中每个字符串的出现位置?

时间:2016-03-10 00:07:16

标签: list haskell

我需要做这样的事情:

func ["a","bb","c","aa","bb","c"] ⟼
  [("a",[0]), ("bb",[1,4]),("c",[2,5]),("aa",3)]

使用此功能:

func :: [String] -> [(String, [Int])]

我在想这样的事情:

func :: [String] -> [(String, [Int])]
func (xs) = func1 (reverse xs)
  where
    func1 [] = []
    func1 (x:xs)
      | notElem x xs = func1 (xs) ++ [(x, [0])]
      | otherwise = func1 (xs)

0只是占位符,如何将索引置于该位置?

4 个答案:

答案 0 :(得分:1)

zip [0..] xs将为您提供元组列表,其中第一项是从零开始的索引,第二项是该索引处的字符串。

答案 1 :(得分:1)

以下是如何使用Data.Map解决此问题的示例。 (我不确定是否有更好的方法来处理zipWith。)

import Data.Map (Map)
import qualified Data.Map as Map

func :: [String] -> [(String, [Int])]
func xs = Map.toList .                          -- [("a", [0]), ("bb", [1,4]), ...]
           (Map.map reverse) .                   -- fromList [("a", [0]), ("bb", [1,4]), ...]
           (Map.fromListWith (++)) $             -- fromList [("a", [0]), ("bb", [4,1]), ...]
           zipWith (\c i -> (c, [i])) xs [0..]   -- [("a", [0]), ("bb", [1]), ...]

这首先是一个元组列表,它将每个字符串与输入中其位置的单例列表配对,然后构建一个映射,其中每个键映射到一个累积的位置列表。累积的位置以相反的顺序构建,Map.map reverse修复,然后从地图中提取所需的列表。

这是一个优化版本,由dfeuer提供。

func = map (\(a,b) -> (a, reverse b)) .
       Map.toList .
       Map.fromListWith (++) .
       zipWith (\i c -> (c, [i])) [0..]

答案 2 :(得分:0)

为了获得索引,你的辅助函数只需要一个额外的参数,你将0传递给最初并在每次递归调用时递增。您可能必须在致电reverse时更改,以便以正确的顺序获取索引!

您必须考虑的第二部分是管理您作为输出创建的对列表的最佳方式。像这样的对的列表有时被称为"关联列表"并且Prelude中有一个lookup函数,您可以使用它来获取与此类结构中的键关联的值。您可以研究该函数并编写update帮助程序,以便更新现有密钥的值。

然而,还有另一种解决问题的一般方法,@ Chad Gilbert在他的回答中暗示了这一点。您可以将解决方案定义为转换组合并将操作组合在问题结构上,而不是直接使用递归。这种形式的解决方案通常结合了映射,过滤,压缩和折叠。我建议练习这两种解决方案。

答案 3 :(得分:0)

我不知道这是否会有所帮助,但这就是我解决问题的方法

zipPos :: [String] -> [(String, Int)]
zipPos xs = zip xs [0..]

singleHead :: ([String], [Int]) -> (String, [Int])
singleHead (x,y) = (head x, y)

filterFstTuple :: (String -> String -> Bool) -> [(String, Int)] -> [(String, Int)]
filterFstTuple _ [] = []
filterFstTuple con xss@(x:_) = filter (con (fst x).fst) xss

getPos :: [(String, Int)] -> [(String, [Int])]
getPos [] = []
getPos xss = singleHead (unzip (filterFstTuple (==) xss)) : getPos  (filterFstTuple (/=) xss)

main :: IO()
main = print . getPos $ zipPos ["a", "b", "c", "c", "d", "a"]