web-dev-qa-db-fra.com

Comment diviser une chaîne en Haskell?

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"].

146
Eric Wilson

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.

124
Jonno_FTW

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"
154
Steve

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"
26
Emmanuel Touzery

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 .

18
evilcandybag

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]
14
antimatter

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.

12
fuz
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"]
9
Frank Meisschaert

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)
6
Robin Begbie

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.

5
Evi1M4chine

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"]
4
fp_mora

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.

2
Irfan Hamid

Exemple dans le ghci:

>  import qualified Text.Regex as R
>  R.splitRegex (R.mkRegex "x") "2x3x777"
>  ["2","3","777"]
1
Andrey