我需要编写一个函数,它可以接收一个由 gggggggeeeeetttttt 组成的字符串,并能够计算每个字母重复出现的次数,并将结果输出为 g7e5t6。
我刚刚开始学习 Haskell,对如何开始完全没有头绪。
我需要编写一个函数,它可以接收一个由 gggggggeeeeetttttt 组成的字符串,并能够计算每个字母重复出现的次数,并将结果输出为 g7e5t6。
我刚刚开始学习 Haskell,对如何开始完全没有头绪。
group
可以将列表中相同的元素分组在一起。由于字符串是字符列表,因此我们有以下代码:group "ggggeeetttt" = ["gggg","eee","tttt"]
map head
,因为head
会获取字符串(或任何列表)的第一个元素:map head ["gggg","eee","tttt"] = ['g','e','t']
map length
来完成:map length ["gggg","eee","tttt"] = [4,3,4]
让我们把这些数字转换回字符串:
map show [4,3,4] = ["4","3","4"]
现在我们需要以某种方式将原始列表和长度列表组合起来。我们可以按照以下步骤进行:
zipWith (:) ['g','e','t'] ["4","3","4"]
zipWith
函数将对两个列表中的成对元素应用指定的函数。这里我使用了 (:)
,它会在列表开头添加一个元素。
这为您提供了实现所需功能的所有构建块。例如,尽管我没有测试过,但以下代码应该可以工作:
f s = concat $ zipWith (:) letters lengths
where
groups = group s
letters = map head groups
lengths = map (show . length) groups
使用推导式的变体,我认为稍微更加美观(考虑之前的解释只是代码):
ghci> let s = "gggggggeeeeetttttt"
ghci> putStrLn $ concat [head g: show (length g) | g <- group s]
g7e5t6
import Data.List
runlength :: String -> String
runlength string = concatMap makeRepetitions (group string)) where
makeRepetitions string = head string : show (length string)
由于太多的括号很让人烦恼,Haskeller们经常使用.
。这个点号将两个函数组合起来创建一个新的函数。f = functionA . functionB
可以理解为f x = functionA (functionB x)
。使用这个点号,我们可以稍微调整程序格式:
import Data.List
runlength :: String -> String
runlength = concatMap makeRepetitions . group where
makeRepetitions string = head string : show (length string)
在我看来,这种表达方式更易读。您可以将runlength
视为管道。首先应用group
,然后我们使用concatMap
将makeRepetitions
映射到输入上,并连接结果。
箭头滥用和点无关 :)
f = group >>> concatMap (head &&& (show . length) >>> uncurry (:))
这里是应用程序:
f = concatMap ((:) <$> head <*> (show . length)) . group