Haskell-在不丢失空格的情况下,将字符串中每个单词的第一个字母大写
我正在做一个练习,要求我编写一个函数,将字符串中所有单词的首字母大写 以下是我迄今为止所做的:Haskell-在不丢失空格的情况下,将字符串中每个单词的第一个字母大写,haskell,Haskell,我正在做一个练习,要求我编写一个函数,将字符串中所有单词的首字母大写 以下是我迄今为止所做的: upperFirst:: String -> String upperFirst str = let upperFirstForEachWord (firstLetter:others) = toUpper firstLetter : map toLower others in unwords (map upp
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
,我失去了开头、结尾和两个空格
如何递归地保存它们(而不处理所有可能的情况)
谢谢你的帮助 模式匹配是你的朋友
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)
例如
减少到
zipWith upper
" a test for upperFirst "
"a test for upperFirst "
如果第一个字符串中的符号是空格,第二个字符串中相同位置的符号是小写字母,则将其设为大写,否则返回相同的值。所以
上部''a'='a'
上部的“a”=''
上部''t'='t'
上部的't'e'='e'
等等。因此,结果是
“对UpperFirst的测试”
而不是像单词那样提取单词,只需拆分字符串,使每个单词的开头也位于列表的开头。正如郝莲所评论的,这可以通过splitOn
来实现,但标准也可以做到这一点:
import Data.List
import Data.Char
upperFirst = concat
. map (\(c:cs) -> toUpper c : cs)
. groupBy (\a b -> isSpace a == isSpace b)
工作原理:它将所有空格或所有非空格的字符分组在一起。然后,它将每个子字符串的开头大写(对空白也这样做有点低效,但无害),然后将所有字符串重新连接在一起
正如user3237465所言,map
和concat
的组合非常常见,并且是某事物的特例。此外,当通过某个谓词进行分组或排序时,还有一种方法非常方便。因此,你也可以这样写
import Control.Monad
import Data.Function
upperFirst = groupBy ((==)`on`isSpace) >=> \(c:cs) -> toUpper c : cs
要加满它,您可以使用修改内容,对于上壳部分:
import Control.Lens
upperFirst = groupBy ((==)`on`isSpace) >=> ix 0 %~ toUpper
此问题是典型的扫描:
>>> scanl (\old new -> if isSpace old then toUpper new else new) ' ' " hello world "
" Hello World "
“种子”的初始状态(这里是一个空格)添加在开头,但尾部在这里是合法的。请参阅scanl
的实现,并将其与具有不同优点的其他扫描功能进行比较。以下是一个简单的版本:
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 "
这与非常相似,但我认为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)
我认为任何使用单词
的解决方案都会遇到这样的问题:单词
和单词
不是完美的反义词,除了双/三等空格外,它们总是会删除前导/尾随空格。尝试编写一个没有单词的解决方案
或unwords
。或者,请参阅,哪个更小心。尝试编写一个递归解决方案,它类似于map
函数,但也传递一段状态shouldnewitnonWhiteSpaceCharBeCapitalized::Bool
,然后查看是否可以在数据中找到一个更高阶的函数。捕获这种递归类型的列表。很好。为什么不concatMap
?@user3237465:我怀疑它是否能增加清晰度,但我喜欢它。这将用空格取代制表符和换行符。哎呀!很抱歉我弄糊涂了。另一种包装方式是drop 1。第一名'。(“”:)
。
>>> titlecase " hello world "
" Hello World "
>>> 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 "
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)