В Haskell, как вы обрезаете пробелы с начала и конца строки?
Как вы обрезаете пробелы с начала и конца строки?
trim " abc "
=>
"abc"
Edit:
Хорошо, позвольте мне быть немного яснее. Я не понимал, что строковые литералы трактуются так иначе, как строки.
Я хотел бы сделать это:
import qualified Data.Text as T
let s :: String = " abc "
in T.strip s
Возможно ли это в Haskell? Я использую -XOverloadedStrings, но это работает только для литералов.
Ответы
Ответ 1
Если у вас есть серьезные потребности в обработке текста, используйте пакет text
из хакажа:
> :set -XOverloadedStrings
> import Data.Text
> strip " abc "
"abc"
Если вы слишком упрямы в использовании text
и не любите неэффективность обратного метода, то, возможно, (и я имею в виду 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 pre и post space) моя обрезка занимает 1,6 мс, обрезка с использованием обратного хода занимает 3,5 мс, а Data.Text.strip
принимает 0,0016 мс...
Ответ 2
От: http://en.wikipedia.org/wiki/Trim_(programming)#Haskell
import Data.Char (isSpace)
trim :: String -> String
trim = f . f
where f = reverse . dropWhile isSpace
Ответ 3
Неэффективно, но легко понять и вставить туда, где необходимо:
strip = lstrip . rstrip
lstrip = dropWhile (`elem` " \t")
rstrip = reverse . lstrip . reverse
Ответ 4
После того, как этот вопрос был задан (около 2012 г.) Data.List
получил dropWhileEnd
, что значительно облегчило:
trim = dropWhileEnd isSpace . dropWhile isSpace
Ответ 5
Конечно, Data.Text лучше для производительности. Но, как уже упоминалось, это просто забавно делать это со списками. Вот версия, которая строит строку в один проход (без обратного и ++) и поддерживает бесконечные списки:
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)
p.s. как для бесконечных списков, которые будут работать:
List.length $ List.take n $ rstrip $ cycle "abc "
и, по очевидной причине, это не будет (будет работать вечно):
List.length $ List.take n $ rstrip $ 'a':(cycle " ")
Ответ 6
Вы можете комбинировать 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"
Ответ 7
Я знаю, что это старый пост, но я не видел решений, которые использовали старый добрый fold
.
Сначала разделите верхнее белое пространство с помощью dropWhile
. Затем, используя foldl'
и простое закрытие, вы можете проанализировать остальную часть строки за один проход и на основе этого анализа передать этот информативный параметр 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"
Ответ 8
Я ничего не знаю о времени выполнения или эффективности, но как насчет этого:
-- 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).
Но я на самом деле этого не знаю, потому что не знаю, как работают функции last и init.;)
Ответ 9
В соответствии с тем, что предложили другие люди, вы можете не менять свою строку, используя:
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
Ответ 10
Другое (std) решение
import System.Environment
import Data.Text
strip :: String -> IO String
strip = return . unpack . Data.Text.strip . pack
main = getLine >>= Main.strip >>= putStrLn
Ответ 11
Это должно быть правильно относительно 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
Ответ 12
В настоящее время пакет MissingH
поставляется с strip
:
import Data.String.Utils
myString = " foo bar "
-- strip :: String -> String
myTrimmedString = strip myString
-- myTrimmedString == "foo bar"
Итак, если преобразование из String
в Text
и обратно не имеет смысла в вашей ситуации, вы можете использовать вышеприведенную функцию.