[英]How to capitalize the first letter of a string and lowercase the rest in Haskell
[英]Haskell - Capitalize the first letter of each word in a string without losing white spaces
我正在做一個練習,要求我編寫一個函數來大寫字符串的所有首字母。
這是我到目前為止所做的:
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
的緣故,我在開頭,結尾處都會丟失白色空格。
如何遞歸保存它們(不處理所有可能的情況)?
感謝您的任何幫助!
您不想像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
的組合非常普遍,而且特殊情況更為常見 。 此外,還有一個很好的小組合器 ,在用一些謂詞進行分組或排序時派上用場。 因此你也可以這樣寫
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
模式匹配是你的朋友
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"
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
例如
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 "
。
這個問題是原型掃描:
>>> 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)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.