В Haskell, как вы обрезаете пробелы от начала и конца строки?

Как обрезать пробелы от начала и конца строки?

trim "  abc " 

=>

"abc"

Edit:

ОК, позвольте мне быть немного яснее. Я не понимал, что строковые литералы рассматриваются так по-разному от строк.

Я хотел бы сделать это:

import qualified Data.Text as T
let s :: String = "  abc  "
in T.strip s

возможно ли это в Haskell? Я использую-XOverloadedStrings, но это работает только для литералов.

12 ответов


если у вас есть серьезные потребности в обработке текста, а затем использовать text пакет от hackage:

> :set -XOverloadedStrings
> import Data.Text
> strip "  abc   "
"abc"

если вы слишком упрямы, чтобы использовать text и не нравится неэффективность обратного метода, тогда, возможно (и я имею в виду, возможно), что-то вроде приведенного ниже будет более эффективным:

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)++ и reverse не имеет большого значения. Но я снова чувствую необходимость сказать ... что если вы действительно беспокоитесь о производительности, то вы не должны использовать String вообще - перейти к Text.

EDIT делая мою точку зрения, быстрый критерий критерия говорит мне, что (для особенно длинной строки слов с пробелами и ~200 до и после пробелов) моя обрезка занимает 1,6 МС, обрезка с использованием обратного занимает 3,5 МС, и Data.Text.strip принимает 0.0016 МС...


From:http://en.wikipedia.org/wiki/Trim_ (Программирование)#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

конечно, данные.Текст лучше для производительности. Но, как уже упоминалось, это просто весело делать это со списками. Вот версия, которая 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)

p.s. что касается бесконечных списков, это будет работать:

List.length $ List.take n $ rstrip $ cycle "abc  "

и, по очевидной причине, это не будет (будет работать вечно):

List.length $ List.take n $ rstrip $ 'a':(cycle " ")

вы можете комбинировать Data.Text ' s 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"

в настоящее время MissingH пакет поставляется с strip функция:

import           Data.String.Utils

myString = "    foo bar    "
-- strip :: String -> String
myTrimmedString = strip myString
-- myTrimmedString == "foo bar"

Итак, если преобразование из String to Text и обратно не имеет смысла в вашей ситуации, вы можете использовать функцию выше.


Я знаю, что это старый пост, но я не видел решений, которые реализовали старый добрый 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"

Я ничего не знаю о времени выполнения или эффективности, но как насчет этого:

-- 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 == ' ')

решение без использования какого-либо другого модуля или библиотеки, кроме прелюдии.

некоторые тесты:

>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. ;)


в соответствии с тем, что предложили другие люди, Вы можете избежать необходимости изменять свою строку с помощью:

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

Это должно быть правильно о (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

другое (std) решение

import System.Environment
import Data.Text

strip :: String -> IO String
strip = return . unpack . Data.Text.strip . pack

main = getLine >>= Main.strip >>= putStrLn