我正在做一个练习,要求我编写一个函数来大写字符串单词的所有首字母。
这是我到目前为止所做的:
upperFirst:: String -> String
upperFirst str = let
upperFirstForEachWord (firstLetter:others) = toUpper firstLetter : map toLower others
in unwords (map upperFirstForEachWord (words str))
这就是它的工作原理:
upperFirst "" = ""
upperFirst "hello friends!" = "Hello Friends!"
但是:
upperFirst " " = ""
upperFirst " a a a " = "A A A"
由于函数words
,我在开头,结尾和双重空格中丢失了空格。
如何递归保存它们(不处理所有可能的情况)?
感谢您的帮助!
答案 0 :(得分:8)
而不是像words
那样提取单词,而是只想拆分字符串,这样每个单词的开头也位于列表的开头。正如郝连评论的那样,这可以用splitOn
来完成,但标准的groupBy
也可以做到这一点:
import Data.List
import Data.Char
upperFirst = concat
. map (\(c:cs) -> toUpper c : cs)
. groupBy (\a b -> isSpace a == isSpace b)
这是如何工作的:它将所有空格或所有非空格的字符组合在一起。然后它会将每个子字符串的开头大写(对于空白也是有效的,但无害),然后将所有字符串连接在一起。
正如user3237465评论的那样,map
和concat
的组合非常常见,并且是even more common的特例。此外,有些a nice little combinator在按某些谓词分组或排序时派上用场。因此,您也可以将其写为
import Control.Monad
import Data.Function
upperFirst = groupBy ((==)`on`isSpace) >=> \(c:cs) -> toUpper c : cs
要充实,您可以使用the hip modern way修改内容,作为大写部分:
import Control.Lens
upperFirst = groupBy ((==)`on`isSpace) >=> ix 0 %~ toUpper
答案 1 :(得分:4)
模式匹配是你的朋友
import Data.Char
upperFirst :: String -> String
upperFirst (c1:c2:rest) =
if isSpace c1 && isLower c2
then c1 : toUpper c2 : upperFirst rest
else c1 : upperFirst (c2:rest)
upperFirst s = s
这个函数的唯一问题是第一个字符不会被大写(也会影响单字符字符串),但如果你真的需要这个功能,那么只需将对此函数的调用包装在另一个处理函数中那些特殊情况:
upperFirst' = <the implementation above>
upperFirst [] = []
upperFirst [c] = [toUpper c] -- No-op on non-letters
upperFirst (s:str) = upperFirst' (toUpper s:str)
测试:
> upperFirst "this is a test of \t this\n function"
"This Is A Test Of \t This\n Function"
答案 2 :(得分:2)
import Data.Char
upperFirst :: String -> String
upperFirst s = zipWith upper (' ':s) s where
upper c1 c2 | isSpace c1 && isLower c2 = toUpper c2
upper c1 c2 = c2
E.g。
upperFirst "a test for upperFirst"
缩减为
zipWith upper
" a test for upperFirst "
"a test for upperFirst "
如果第一个字符串中的符号是空格而第二个字符串中相同位置的符号是小写字母,则将其设为大写,否则返回相同的符号。所以
upper ' ' 'a' = 'A'
upper 'a' ' ' = ' '
upper ' ' 't' = 'T'
upper 't' 'e' = 'e'
等等。因此结果是"A Test For UpperFirst "
。
答案 3 :(得分:1)
这个问题是原型扫描:
>>> scanl (\old new -> if isSpace old then toUpper new else new) ' ' " hello world "
" Hello World "
“种子”的初始状态 - 这里是一个空格 - 在开头添加,但尾部在这里是合法的。请参阅scanl
here的实现,并将其与其他具有不同优势的扫描函数进行比较。这是一个简单的版本:
scan op state [] = state : []
scan op state (x:xs) = state : scan op (op state x) xs
您可以通过内联编写功能
op old new = if isSpace old then toUpper new else new
进入定义
myscan state [] = state : []
myscan state (x:xs) = state : myscan (if isSpace state then toUpper x else x) xs
用空格开始然后取尾:
titlecase = tail . myscan ' '
然后在ghci我看到了
>>> titlecase " hello world "
" Hello World "
或者您可以直接使用我们定义的一般scan
>>> let op old new = if isSpace old then toUpper new else new
>>> scan op ' ' " hello world "
" Hello World "
>>> tail $ scan op ' ' " hello world "
" Hello World "
答案 4 :(得分:0)
这与Michael's solution非常相似,但我认为mapAccumL
比scanl
更合适:
upperFirst :: Traversable t => t Char -> t Char
upperFirst = snd . mapAccumL go True where
go lastSpace x
| isSpace x = (True, x)
| lastSpace = (,) False $! toUpper x
| otherwise = (False, x)