Existe-t-il un moyen standard de diviser une chaîne en Haskell?
lines
et words
fonctionnent très bien en séparant un espace ou une nouvelle ligne, mais il existe sûrement un moyen standard de diviser par une virgule?
Je ne pouvais pas le trouver sur Hoogle.
Pour être précis, je cherche quelque chose où split "," "my,comma,separated,list"
résultats ["my","comma","separated","list"]
.
Il existe un paquet pour cela appelé split .
cabal install split
Utilisez-le comme ceci:
ghci> import Data.List.Split
ghci> splitOn "," "my,comma,separated,list"
["my","comma","separated","list"]
Il comporte de nombreuses autres fonctions permettant de diviser des délimiteurs correspondants ou d’avoir plusieurs délimiteurs.
Rappelez-vous que vous pouvez consulter la définition des fonctions Prelude!
http://www.haskell.org/onlinereport/standard-prelude.html
En regardant là-bas, la définition de words
est,
words :: String -> [String]
words s = case dropWhile Char.isSpace s of
"" -> []
s' -> w : words s''
where (w, s'') = break Char.isSpace s'
Alors changez-le pour une fonction qui prend un prédicat:
wordsWhen :: (Char -> Bool) -> String -> [String]
wordsWhen p s = case dropWhile p s of
"" -> []
s' -> w : wordsWhen p s''
where (w, s'') = break p s'
Puis appelez-le avec le prédicat que vous voulez!
main = print $ wordsWhen (==',') "break,this,string,at,commas"
Si vous utilisez Data.Text, il y a splitOn:
http://hackage.haskell.org/packages/archive/text/0.11.2.0/doc/html/Data-Text.html#v:splitOn
Ceci est construit dans la plate-forme Haskell.
Donc par exemple:
import qualified Data.Text as T
main = print $ T.splitOn (T.pack " ") (T.pack "this is a test")
ou:
{-# LANGUAGE OverloadedStrings #-}
import qualified Data.Text as T
main = print $ T.splitOn " " "this is a test"
Dans le module Text.Regex (partie de la plate-forme Haskell), il y a une fonction:
splitRegex :: Regex -> String -> [String]
qui divise une chaîne basée sur une expression régulière. L'API peut être trouvé à Hackage .
Utilisation Data.List.Split
, qui utilise split
:
[me@localhost]$ ghci
Prelude> import Data.List.Split
Prelude Data.List.Split> let l = splitOn "," "1,2,3,4"
Prelude Data.List.Split> :t l
l :: [[Char]]
Prelude Data.List.Split> l
["1","2","3","4"]
Prelude Data.List.Split> let { convert :: [String] -> [Integer]; convert = map read }
Prelude Data.List.Split> let l2 = convert l
Prelude Data.List.Split> :t l2
l2 :: [Integer]
Prelude Data.List.Split> l2
[1,2,3,4]
Essaye celui-là:
import Data.List (unfoldr)
separateBy :: Eq a => a -> [a] -> [[a]]
separateBy chr = unfoldr sep where
sep [] = Nothing
sep l = Just . fmap (drop 1) . break (== chr) $ l
Ne fonctionne que pour un seul caractère, mais devrait être facilement extensible.
split :: Eq a => a -> [a] -> [[a]]
split d [] = []
split d s = x : split d (drop 1 y) where (x,y) = span (/= d) s
Par exemple.
split ';' "a;bb;ccc;;d"
> ["a","bb","ccc","","d"]
Un seul délimiteur de fin sera supprimé:
split ';' "a;bb;ccc;;d;"
> ["a","bb","ccc","","d"]
J'ai commencé à apprendre Haskell hier, alors corrigez-moi si je me trompe mais:
split :: Eq a => a -> [a] -> [[a]]
split x y = func x y [[]]
where
func x [] z = reverse $ map (reverse) z
func x (y:ys) (z:zs) = if y==x then
func x ys ([]:(z:zs))
else
func x ys ((y:z):zs)
donne:
*Main> split ' ' "this is a test"
["this","is","a","test"]
ou peut-être que tu voulais
*Main> splitWithStr " and " "this and is and a and test"
["this","is","a","test"]
ce qui serait:
splitWithStr :: Eq a => [a] -> [a] -> [[a]]
splitWithStr x y = func x y [[]]
where
func x [] z = reverse $ map (reverse) z
func x (y:ys) (z:zs) = if (take (length x) (y:ys)) == x then
func x (drop (length x) (y:ys)) ([]:(z:zs))
else
func x ys ((y:z):zs)
Je ne sais pas comment ajouter un commentaire à la réponse de Steve, mais je voudrais recommander le
documentation des bibliothèques GHC ,
et plus précisément là
Fonctions de sous-liste dans Data.List
Ce qui est beaucoup mieux comme référence, que de simplement lire le rapport Haskell en clair.
Généralement, un pli avec une règle sur le moment de créer une nouvelle sous-liste à alimenter devrait également le résoudre.
Sans rien importer d'une simple substitution d'un caractère à un espace, le séparateur de cible pour words
est un espace. Quelque chose comme:
words [if c == ',' then ' ' else c|c <- "my,comma,separated,list"]
ou
words let f ',' = ' '; f c = c in map f "my,comma,separated,list"
Vous pouvez en faire une fonction avec des paramètres. Vous pouvez éliminer le paramètre character-to-match mon nombre correspondant, comme dans:
[if elem c ";,.:-+@!$#?" then ' ' else c|c <-"my,comma;separated!list"]
En plus des fonctions efficaces et prédéfinies fournies dans les réponses, je vais ajouter les miennes qui font simplement partie de mon répertoire de fonctions Haskell que j'écrivais pour apprendre la langue de mon temps libre:
-- Correct but inefficient implementation
wordsBy :: String -> Char -> [String]
wordsBy s c = reverse (go s []) where
go s' ws = case (dropWhile (\c' -> c' == c) s') of
"" -> ws
rem -> go ((dropWhile (\c' -> c' /= c) rem)) ((takeWhile (\c' -> c' /= c) rem) : ws)
-- Breaks up by predicate function to allow for more complex conditions (\c -> c == ',' || c == ';')
wordsByF :: String -> (Char -> Bool) -> [String]
wordsByF s f = reverse (go s []) where
go s' ws = case ((dropWhile (\c' -> f c')) s') of
"" -> ws
rem -> go ((dropWhile (\c' -> (f c') == False)) rem) (((takeWhile (\c' -> (f c') == False)) rem) : ws)
Les solutions sont au moins récursives afin d'éviter tout débordement de pile.
Exemple dans le ghci:
> import qualified Text.Regex as R
> R.splitRegex (R.mkRegex "x") "2x3x777"
> ["2","3","777"]