List 如何计算列表中每个字符串的出现次数及其位置?
我需要做这样的事情:List 如何计算列表中每个字符串的出现次数及其位置?,list,haskell,List,Haskell,我需要做这样的事情: func ["a","bb","c","aa","bb","c"] ⟼ [("a",[0]), ("bb",[1,4]),("c",[2,5]),("aa",3)] func :: [String] -> [(String, [Int])] func (xs) = func1 (reverse xs) where func1 [] = [] func1 (x:xs) | notElem x xs = func1 (xs) ++ [
func ["a","bb","c","aa","bb","c"] ⟼
[("a",[0]), ("bb",[1,4]),("c",[2,5]),("aa",3)]
func :: [String] -> [(String, [Int])]
func (xs) = func1 (reverse xs)
where
func1 [] = []
func1 (x:xs)
| notElem x xs = func1 (xs) ++ [(x, [0])]
| otherwise = func1 (xs)
使用此功能:
func :: [String] -> [(String, [Int])]
我是这样想的:
func ["a","bb","c","aa","bb","c"] ⟼
[("a",[0]), ("bb",[1,4]),("c",[2,5]),("aa",3)]
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只是一个占位符,如何将索引放在该位置?
zip[0..]xs
将为您提供一个元组列表,其中第一项是基于零的索引,第二项是该索引处的字符串。zip[0..]xs将为您提供一个元组列表,其中第一项是从零开始的索引,第二项是该索引处的字符串。为了获得索引,您的助手函数只需要一个额外的参数,您可以将0
传递到初始值,并在每次递归调用时递增。但是,当您调用reverse
时,您可能必须进行更改,以便以正确的顺序获取索引
第二部分你必须考虑的是管理你正在创建的成对的列表作为输出的最好方法。这样的成对列表有时称为“关联列表”,前奏曲中有一个
查找
函数,您可以使用该函数获取与此类结构中的键关联的值。您可以研究该函数,并编写一个update
helper来附带它,以便更新现有键处的值
然而,查德·吉尔伯特在他的回答中暗示了另一种解决问题的一般方法。您通常可以将解决方案定义为转换和问题结构上的组合操作,而不是直接使用递归。这种形式的解决方案通常结合映射、过滤、压缩和折叠。我建议您使用这两种解决方案进行练习。为了获得索引,您的helper函数只需要一个额外的参数,您可以首先将0
传递给它,然后在每次递归调用时递增。但是,当您调用reverse
时,您可能必须进行更改,以便以正确的顺序获取索引
第二部分你必须考虑的是管理你正在创建的成对的列表作为输出的最好方法。这样的成对列表有时称为“关联列表”,前奏曲中有一个
查找
函数,您可以使用该函数获取与此类结构中的键关联的值。您可以研究该函数,并编写一个update
helper来附带它,以便更新现有键处的值
然而,查德·吉尔伯特在他的回答中暗示了另一种解决问题的一般方法。您通常可以将解决方案定义为转换和问题结构上的组合操作,而不是直接使用递归。这种形式的解决方案通常结合映射、过滤、压缩和折叠。我建议两种方法都练习。我不知道这是否有帮助,但这就是我解决问题的方法
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"]
我不知道这是否有帮助,但这就是我解决问题的方法
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"]
下面是一个如何使用Data.Map
解决此问题的示例。(我不确定是否有更好的方法处理zipWith
)
这从一个元组列表开始,将每个字符串配对到其在输入中的位置的单例列表,然后构建一个映射,其中每个键映射到一个累积的位置列表。累积位置按相反顺序建立,这将Map.Map reverse
修复,然后从地图中提取所需列表
这是一个优化版本,由dfeuer提供
func = map (\(a,b) -> (a, reverse b)) .
Map.toList .
Map.fromListWith (++) .
zipWith (\i c -> (c, [i])) [0..]
下面是一个如何使用Data.Map
解决此问题的示例。(我不确定是否有更好的方法处理zipWith
)
这从一个元组列表开始,将每个字符串配对到其在输入中的位置的单例列表,然后构建一个映射,其中每个键映射到一个累积的位置列表。累积位置按相反顺序建立,这将Map.Map reverse
修复,然后从地图中提取所需列表
这是一个优化版本,由dfeuer提供
func = map (\(a,b) -> (a, reverse b)) .
Map.toList .
Map.fromListWith (++) .
zipWith (\i c -> (c, [i])) [0..]
如果您没有被限制使用列表作为结果数据类型,我建议您切换到数据.Map
-然后insertWith
功能非常方便-特别是如果您没有被限制使用列表作为结果数据类型I,请在@Chad gilbert推荐的zip
上使用折叠
建议您切换到Data.Map
-然后使用insertWith
功能非常方便-尤其是在@Chad gilbert推荐的zip
上加上折叠
,这是非常不清楚的,人们普遍不鼓励使用head
。人们已经假设元组中有一个值,并且连接的值是相同的,那么,如果可以得到head,为什么还要折叠相同的值呢。这就是我的逻辑,我不知道其他人会怎么做。但在解释代码时,我想我会编辑帖子。在某些情况下,为了提高效率,程序会跟踪一些有关结构大小/形状的冗余信息。在这种情况下,效率可能要求程序信任冗余信息,以证明其他部分操作的合理性。我现在想到的例子是在最近版本的Data.Sequence
中实现zipWith
,但我知道我见过其他的例子。然而,在你的情况下,没有具体的需要依赖于头。啊,是的,我记得另一个例子:乌龟和兔子列表分割。如果兔子还没有到达列表的末尾,你可以相信乌龟也没有这样做。这一点很不清楚,而且使用head
被广泛地劝阻。人们已经假设元组中有一个值,并且连接的值是相同的,那么如果你能得到head,为什么Travel会折叠相同的值呢。这就是逻辑