string - listas - multiplos en haskell
¿Cómo dividir una cadena en Haskell? (12)
¿Hay una forma estándar de dividir una cadena en Haskell?
lines
y las words
funcionan bien si se dividen en un espacio o línea nueva, pero seguramente hay una forma estándar de dividir en una coma. No pude encontrarlo en Hoogle?
Para ser específico, estoy buscando algo donde split "," "my,comma,separated,list"
devuelve ["my","comma","separated","list"]
Gracias.
¡Recuerde que puede buscar la definición de funciones de Prelude!
http://www.haskell.org/onlinereport/standard-prelude.html
Mirando allí, la definición de words
es,
words :: String -> [String]
words s = case dropWhile Char.isSpace s of
"" -> []
s'' -> w : words s''''
where (w, s'''') = break Char.isSpace s''
Por lo tanto, cámbielo por una función que tenga un predicado:
wordsWhen :: (Char -> Bool) -> String -> [String]
wordsWhen p s = case dropWhile p s of
"" -> []
s'' -> w : wordsWhen p s''''
where (w, s'''') = break p s''
¡Entonces llámalo con el predicado que quieras!
main = print $ wordsWhen (=='','') "break,this,string,at,commas"
Además de las funciones eficientes y preconstruidas dadas en las respuestas, agregaré las mías que son simplemente parte de mi repertorio de funciones de Haskell que estaba escribiendo para aprender el idioma en mi propio tiempo:
-- 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)
Las soluciones son al menos recursivas de cola para que no incurran en un desbordamiento de pila.
Ejemplo en el ghci:
> import qualified Text.Regex as R
> R.splitRegex (R.mkRegex "x") "2x3x777"
> ["2","3","777"]
Empecé a aprender Haskell ayer, así que corrígeme si estoy equivocado pero:
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)
da:
*Main> split '' '' "this is a test"
["this","is","a","test"]
o tal vez querías
*Main> splitWithStr " and " "this and is and a and test"
["this","is","a","test"]
cuál podría ser:
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)
En el módulo Text.Regex (parte de la Plataforma Haskell), hay una función:
splitRegex :: Regex -> String -> [String]
que divide una cadena basada en una expresión regular. La API se puede encontrar en Hackage .
Hay un paquete para esto llamado split .
cabal install split
Úselo así:
ghci> import Data.List.Split
ghci> splitOn "," "my,comma,separated,list"
["my","comma","separated","list"]
Viene con muchas otras funciones para dividir en delimitadores coincidentes o tener varios delimitadores.
No sé cómo agregar un comentario en la respuesta de Steve, pero me gustaría recomendar el
Documentación de las bibliotecas de GHC
y allí específicamente el
Funciones de sublista en Data.List
Lo cual es mucho mejor como referencia, que solo leer el informe simple de Haskell.
Genéricamente, un fold con una regla sobre cuándo crear una nueva sublista para alimentar, debería resolverlo también.
Prueba este:
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
Solo funciona para un solo char, pero debe poder extenderse fácilmente.
Si usa Data.Text, hay splitOn:
http://hackage.haskell.org/packages/archive/text/0.11.2.0/doc/html/Data-Text.html#v:splitOn
Esto está construido en la plataforma Haskell.
Entonces, por ejemplo:
import qualified Data.Text as T
main = print $ T.splitOn (T.pack " ") (T.pack "this is a test")
o:
{-# LANGUAGE OverloadedStrings #-}
import qualified Data.Text as T
main = print $ T.splitOn " " "this is a test"
Sin importar nada una sustitución directa de un carácter por un espacio, el separador de destino para words
es un espacio. Algo como:
words [if c == '','' then '' '' else c|c <- "my,comma,separated,list"]
o
words let f '','' = '' ''; f c = c in map f "my,comma,separated,list"
Puede convertir esto en una función con parámetros. Puedes eliminar el parámetro carácter a coincidencia mis muchos coincidentes, como en:
[if elem c ";,.:-+@!$#?" then '' '' else c|c <-"my,comma;separated!list"]
Use Data.List.Split
, que usa 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]
split :: Eq a => a -> [a] -> [[a]]
split d [] = []
split d s = x : split d (drop 1 y) where (x,y) = span (/= d) s
P.ej
split '';'' "a;bb;ccc;;d"
> ["a","bb","ccc","","d"]
Se eliminará un único delimitador final:
split '';'' "a;bb;ccc;;d;"
> ["a","bb","ccc","","d"]