文字列の先頭と末尾から空白をどのようにトリミングしますか?
trim " abc "
=>
"abc"
編集:
わかりました、もう少しはっきりさせてください。私は、文字列リテラルが文字列と非常に異なって扱われることを理解していませんでした。
私はこれをやりたいです:
import qualified Data.Text as T
let s :: String = " abc "
in T.strip s
Haskellでこれは可能ですか? -XOverloadedStringsを使用していますが、それはリテラルに対してのみ機能するようです。
深刻なテキスト処理が必要な場合は、hackageのtext
パッケージを使用します。
> :set -XOverloadedStrings
> import Data.Text
> strip " abc "
"abc"
text
を使用するには頑固すぎて、reverseメソッドの非効率性が気に入らない場合は、おそらく(そしてMAYBEを意味する)次のようなものがより効率的になります。
import Data.Char
trim xs = dropSpaceTail "" $ dropWhile isSpace xs
dropSpaceTail maybeStuff "" = ""
dropSpaceTail maybeStuff (x:xs)
| isSpace x = dropSpaceTail (x:maybeStuff) xs
| null maybeStuff = x : dropSpaceTail "" xs
| otherwise = reverse maybeStuff ++ x : dropSpaceTail "" xs
> trim " hello this \t should trim ok.. .I think .. \t "
"hello this \t should trim ok.. .I think .."
これは、スペースの長さが最小限であるという前提で書いたので、O(n) of ++
およびreverse
はほとんど問題になりません。ただし、実際にパフォーマンスに懸念がある場合は、String
を使用しないでください-Text
に移動してください。
EDIT Criterionベンチマークは、クイック基準ベンチマークは(スペースと〜200前後のスペースを持つ特に長い文字列の場合)トリムが1.6ミリ秒かかり、リバースを使用したトリムが3.5ミリ秒かかり、Data.Text.strip
0.0016 msかかります...
From: http://en.wikipedia.org/wiki/Trim_(programming)#Haskell
import Data.Char (isSpace)
trim :: String -> String
trim = f . f
where f = reverse . dropWhile isSpace
この質問が行われた後(2012年頃)Data.List
dropWhileEnd
を得たので、これがずっと簡単になりました。
trim = dropWhileEnd isSpace . dropWhile isSpace
効率的ではないが、理解しやすく、必要な場所に貼り付ける:
strip = lstrip . rstrip
lstrip = dropWhile (`elem` " \t")
rstrip = reverse . lstrip . reverse
確かに、Data.Textはパフォーマンスに優れています。しかし、前述したように、リストを使用してそれを行うのは楽しいだけです。これは、rstripのシングルパス(リバースと++なし)の文字列であり、無限リストをサポートするバージョンです。
rstrip :: String -> String
rstrip str = let (zs, f) = go str in if f then [] else zs
where
go [] = ([], True)
go (y:ys) =
if isSpace y then
let (zs, f) = go ys in (y:zs, f)
else
(y:(rstrip ys), False)
追伸無限リストに関しては、それは動作します:
List.length $ List.take n $ rstrip $ cycle "abc "
そして、明白な理由で、それはしません(永久に実行されます):
List.length $ List.take n $ rstrip $ 'a':(cycle " ")
現在、MissingH
パッケージには strip
関数が付属しています。
import Data.String.Utils
myString = " foo bar "
-- strip :: String -> String
myTrimmedString = strip myString
-- myTrimmedString == "foo bar"
そのため、String
からText
への変換とその逆の変換が意味をなさない場合は、上記の関数を使用できます。
Data.Text
のstrip
には、文字列のオーバーロードを避けるためのun/packing関数があります。
import qualified Data.Text as T
strip = T.unpack . T.strip . T.pack
lstrip = T.unpack . T.stripStart . T.pack
rstrip = T.unpack . T.stripEnd . T.pack
それをテストする:
> let s = " hello "
> strip s
"hello"
> lstrip s
"hello "
> rstrip s
" hello"
これが古い投稿であることは知っていますが、古き良きfold
を実装したソリューションは見当たりませんでした。
最初に、dropWhile
を使用して先頭の空白を削除します。次に、foldl'
と単純なクロージャを使用すると、1回のパスで残りの文字列を分析し、その分析に基づいて、take
を必要とせずに、その情報パラメータをreverse
に渡すことができます。
import Data.Char (isSpace)
import Data.List (foldl')
trim :: String -> String
trim s = let
s' = dropWhile isSpace s
trim' = foldl'
(\(c,w) x -> if isSpace x then (c,w+1)
else (c+w+1,0)) (0,0) s'
in
take (fst trim') s'
変数c
は、吸収されるべき空白と非空白の組み合わせを追跡し、変数w
は、削除される右側の空白を追跡します。
テスト実行:
print $ trim " a b c "
print $ trim " ab c "
print $ trim " abc "
print $ trim "abc"
print $ trim "a bc "
出力:
"a b c"
"ab c"
"abc"
"abc"
"a bc"
これはO(n)について正しいはずです、私は信じています:
import Data.Char (isSpace)
trim :: String -> String
-- Trimming the front is easy. Use a helper for the end.
trim = dropWhile isSpace . trim' []
where
trim' :: String -> String -> String
-- When finding whitespace, put it in the space bin. When finding
-- non-whitespace, include the binned whitespace and continue with an
-- empty bin. When at the end, just throw away the bin.
trim' _ [] = []
trim' bin (a:as) | isSpace a = trim' (bin ++ [a]) as
| otherwise = bin ++ a : trim' [] as
他の人が提案していることに沿って、次を使用して文字列を逆にする必要を回避できます。
import Data.Char (isSpace)
dropFromTailWhile _ [] = []
dropFromTailWhile p item
| p (last items) = dropFromTailWhile p $ init items
| otherwise = items
trim :: String -> String
trim = dropFromTailWhile isSpace . dropWhile isSpace
ランタイムや効率については何も知りませんが、これについてはどうですか:
-- entirely input is to be trimmed
trim :: String -> String
trim = Prelude.filter (not . isSpace')
-- just the left and the right side of the input is to be trimmed
lrtrim :: String -> String
lrtrim = \xs -> rtrim $ ltrim xs
where
ltrim = dropWhile (isSpace')
rtrim xs
| Prelude.null xs = []
| otherwise = if isSpace' $ last xs
then rtrim $ init xs
else xs
-- returns True if input equals ' '
isSpace' :: Char -> Bool
isSpace' = \c -> (c == ' ')
Prelude以外のモジュールやライブラリを使用しないソリューション。
いくつかのテスト:
>lrtrim ""
>""
>lrtrim " "
>""
>lrtrim "haskell "
>"haskell"
>lrtrim " haskell "
>"haskell"
>lrtrim " h a s k e ll "
>"h a s k e ll"
ランタイムO(n)である可能性があります。
しかし、実際には、関数のランタイムとinitのランタイムを知らないため、それを知りません。 ;)