将大写字母转换为小写字母,并计算Haskell中的字符串
我是Haskell的新手,我正在尝试将输入的字符串转换为小写字母。转换后,我想对所有来自['a'..'z']的小写字母进行计数 例如:“这是一个TES3T” 结果:[(t,3)、(h,1)、(i,2)、(s,2)、(a,1)(e,1)] 这就是我到目前为止所做的:将大写字母转换为小写字母,并计算Haskell中的字符串,haskell,Haskell,我是Haskell的新手,我正在尝试将输入的字符串转换为小写字母。转换后,我想对所有来自['a'..'z']的小写字母进行计数 例如:“这是一个TES3T” 结果:[(t,3)、(h,1)、(i,2)、(s,2)、(a,1)(e,1)] 这就是我到目前为止所做的: countL :: [Char] -> Char -> Int countL s c = length ( [x | x <- s, x == c]) letter_count :: [Char] -> [(
countL :: [Char] -> Char -> Int
countL s c = length ( [x | x <- s, x == c])
letter_count :: [Char] -> [(Char, Int)]
letter_count s = nub [(c, countL s c) | c <- s]
countL::[Char]->Char->Int
countL s c=长度([x | x[(字符,整数)]
字母_counts=nub[(c,countL s c)| c[Char]
toLowerString str=[toLower x | x解决方案中有一些优化空间
首先函数letter_count
在O(n^2)时间内运行:当[(c,countL s c)| c的结果出现时,解决方案中有一些优化空间
首先函数letter_count
在O(n^2)时间内运行:当[(c,countL s c)| c的结果出现时,使用模块Data.List中的函数group
()。它接受一个列表并返回其分组元素的列表。要获得唯一的组,只需先对输入字符串排序。例如:
group (sort "Hello World")
将为您提供:
[" ","H","W","d","e","lll","oo","r"]
您只需将这些子字符串中的每一个子字符串转换为其长度和第一个字母的元组:
map (\s -> (head s, length s))
因此,您的字母计数将是:
letter_count :: [Char] -> [(Char, Int)]
letter_count = map (\s -> (head s, length s)) . group . sort
计数字母的一个简单易行的方法是使用模块Data.List
()中的函数group
。它获取一个列表并返回其分组元素的列表。要获得唯一的组,只需先对输入字符串排序。例如:
group (sort "Hello World")
将为您提供:
[" ","H","W","d","e","lll","oo","r"]
您只需将这些子字符串中的每一个子字符串转换为其长度和第一个字母的元组:
map (\s -> (head s, length s))
因此,您的字母计数将是:
letter_count :: [Char] -> [(Char, Int)]
letter_count = map (\s -> (head s, length s)) . group . sort
Radek提到的阵列解决方案如下所示:
toLowerString :: [Char] -> [Char]
toLowerString str = map toLower str
groupedStr = (group . sort . map toLower . filter isLetter) str
import qualified Data.Array.Unboxed as A
import Data.Char ( isAsciiLower )
countLettersArr :: [Char] -> [(Char, Int)]
countLettersArr cs = filter ((/= 0) . snd) (A.assocs arr)
where
arr :: A.UArray Char Int
arr = A.accumArray (+) 0 ('a', 'z')
[(c, 1 :: Int) | c <- cs, isAsciiLower c]
一种效率稍低的方法是使用IntMap
而不是数组。这有点痛苦,因为IntMap
只接受Int
索引,但其工作方式几乎相同:fromListWith
与accumArray
非常相似。此版本将记录您输入的任何字符不管是否使用小写字母。您可以通过在im
定义中的列表理解中添加一个保护来轻松改变这一点
import qualified Data.IntMap.Strict as M
import Data.Char (ord, chr)
import Data.Bifunctor (first)
-- Count how many of each letter appear in a string.
countLettersIM :: [Char] -> [(Char, Int)]
countLettersIM cs = map (first chr) . M.toList $ im
where
im :: M.IntMap Int
im = M.fromListWith (+) [(ord c, 1) | c <- cs]
import qualified Data.IntMap.Strict作为M
导入数据.Char(ord,chr)
导入数据.Bifunctor(第一个)
--计算每个字母在一个字符串中出现的数量。
countLettersIM::[Char]->[(Char,Int)]
countlettsim cs=map(第一个chr).M.toList$im
哪里
im::M.IntMap Int
im=M.fromListWith(+)[(ord c,1)| c[a]->[(a,Int)]
countEnumsIM cs=map(first-toEnum).M.toList$im
哪里
im=M.fromListWith(+)[(fromnum c,1)| cRadek提到的阵列解决方案如下所示:
toLowerString :: [Char] -> [Char]
toLowerString str = map toLower str
groupedStr = (group . sort . map toLower . filter isLetter) str
import qualified Data.Array.Unboxed as A
import Data.Char ( isAsciiLower )
countLettersArr :: [Char] -> [(Char, Int)]
countLettersArr cs = filter ((/= 0) . snd) (A.assocs arr)
where
arr :: A.UArray Char Int
arr = A.accumArray (+) 0 ('a', 'z')
[(c, 1 :: Int) | c <- cs, isAsciiLower c]
一种效率稍低的方法是使用IntMap
而不是数组。这有点痛苦,因为IntMap
只接受Int
索引,但其工作方式几乎相同:fromListWith
与accumArray
非常相似。此版本将记录您输入的任何字符不管是否使用小写字母。您可以通过在im
定义中的列表理解中添加一个保护来轻松改变这一点
import qualified Data.IntMap.Strict as M
import Data.Char (ord, chr)
import Data.Bifunctor (first)
-- Count how many of each letter appear in a string.
countLettersIM :: [Char] -> [(Char, Int)]
countLettersIM cs = map (first chr) . M.toList $ im
where
im :: M.IntMap Int
im = M.fromListWith (+) [(ord c, 1) | c <- cs]
import qualified Data.IntMap.Strict作为M
导入数据.Char(ord,chr)
导入数据.Bifunctor(第一个)
--计算每个字母在一个字符串中出现的数量。
countLettersIM::[Char]->[(Char,Int)]
countlettsim cs=map(第一个chr).M.toList$im
哪里
im::M.IntMap Int
im=M.fromListWith(+)[(ord c,1)| c[a]->[(a,Int)]
countEnumsIM cs=map(first-toEnum).M.toList$im
哪里
im=M.fromListWith(+)[(fromnum c,1)| c TrytoLowerAndFilterString=filter(`elem`['a'..'z'])。映射到lower
.fyi,Data.List.Utils.countElem
TrytoLowerAndFilterString=filter(`elem`['a'..'z'])).map toLower
.fyi,Data.List.Utils.countElem
从列表理解转换为map
不是一种优化;编译后的代码(以及性能)应该是相同的。但更清楚。你是对的,我的意思是。也许我不够清楚。感谢@Radek的非常好的解释。如果我决定使用百分比而不是计数。即。[(t,0.25),(h,0.12)]如t=25%和h=12%。我可以使用类似的方法吗?当必须将函数组合在一起时,我感到困惑,而且您的方法似乎非常清楚。是的,您可以轻松修改我的解决方案来计算百分比。您想计算与字母数的字符总数相关的百分比吗?例如, aaaa1234
字母a应该得到50%或100%?如果你让我知道,我会扩展我的答案。简短的解释:要得到百分比,基本上你所要做的就是计算str
或filteredStr
的长度(取决于你是否想要考虑非字母符号——50%对100%以上):lengthOfStr=length str
然后将最后一个表达式修改如下:[(h,from integral(length fragment)/lengthOfStr)| fragment@(h:_)从列表理解切换到map
不是优化;编译后的代码(以及性能)应该是相同的。但更清楚。你是对的,我的意思是。也许我不够清楚。感谢@Radek的非常好的解释。如果我决定使用百分比而不是计数。即。[(t,0.25),(h,0.12)]如t=25%和h=12%。我可以使用类似的方法吗?当必须将函数组合在一起时,我感到困惑,而且您的方法似乎非常清楚。是的,您可以轻松修改我的解决方案来计算百分比。是否要计算与函数的总字符数相关的百分比
countEnumsIM :: Enum a => [a] -> [(a, Int)]
countEnumsIM cs = map (first toEnum) . M.toList $ im
where
im = M.fromListWith (+) [(fromEnum c,1) | c <- cs]