하스켈에서 줄을 나누는 방법?


163

Haskell에서 문자열을 분리하는 표준 방법이 있습니까?

lineswords업무 공백이나 줄 바꿈에 분할에서 큰하지만 반드시 쉼표에 분할하는 표준 방법은 무엇입니까?

Hoogle에서 찾을 수 없습니다.

구체적으로, split "," "my,comma,separated,list"returns 가있는 곳을 찾고 ["my","comma","separated","list"]있습니다.


21
나는 장래의 릴리스 Data.List또는 심지어 이러한 기능을 정말로 원합니다 Prelude. 코드 골프에 사용할 수 없다면 너무 흔하고 불쾌합니다.
fuz

답변:


135

이를 위해 split 이라는 패키지가 있습니다 .

cabal install split

다음과 같이 사용하십시오.

ghci> import Data.List.Split
ghci> splitOn "," "my,comma,separated,list"
["my","comma","separated","list"]

일치하는 구분 기호를 분할하거나 여러 구분 기호를 갖는 많은 다른 기능이 제공됩니다.


9
멋있는. 이 패키지를 몰랐습니다. 이것은 작업에 대한 많은 제어 기능을 제공 하므로 최종 분할 패키지입니다 (결과 공간 잘라 내기, 결과 구분 기호 남김, 연속 구분 기호 제거 등). 리스트를 나누는 방법은 너무나 많기 때문에 split모든 요구에 부응하는 단일 기능 을 가질 수 없으며 , 실제로 그런 종류의 패키지가 필요합니다.
gawi

1
외부 패키지를 허용, 그렇지 않은 경우, MissingH 또한 분할 기능을 제공합니다 : hackage.haskell.org/packages/archive/MissingH/1.2.0.0/doc/html/... 패키지는 다른 "좋은 -에 -이"기능을 많이 제공 나는 꽤 많은 패키지가 그것에 의존한다는 것을 알았습니다.
Emmanuel Touzery

41
분할 패키지는 이제 최신 릴리스에서 하스켈 플랫폼과 분리되었습니다.
인터넷

14
Data.List.Split (splitOn)을 가져 와서 마을로갑니다. splitOn :: Eq a => [a]-> [a]-> [[a]]
인터넷

1
@RussAbbott 분할 패키지는 다운로드 할 때 Haskell 플랫폼에 포함되어 있지만 ( haskell.org/platform/contents.html ) 프로젝트를 빌드 할 때 자동으로로드되지 않습니다. cabal 파일 splitbuild-depends목록에 추가 하십시오. 예를 들어 프로젝트가 hello 인 경우 hello.cabal, executable hello라인 아래 파일에 `build-depends : base, split`과 같은 줄을 두십시오 (두 칸 들여 쓰기 참고). 그런 다음 cabal build명령을 사용하여 빌드하십시오 . Cf. haskell.org/cabal/users-guide/…
expz

164

Prelude 기능의 정의를 찾을 수 있습니다!

http://www.haskell.org/onlinereport/standard-prelude.html

거기를 보면 wordsis 의 정의 는

words   :: String -> [String]
words s =  case dropWhile Char.isSpace s of
                      "" -> []
                      s' -> w : words s''
                            where (w, s'') = break Char.isSpace s'

술어를 취하는 함수로 변경하십시오.

wordsWhen     :: (Char -> Bool) -> String -> [String]
wordsWhen p s =  case dropWhile p s of
                      "" -> []
                      s' -> w : wordsWhen p s''
                            where (w, s'') = break p s'

그런 다음 원하는 술어를 사용하여 호출하십시오!

main = print $ wordsWhen (==',') "break,this,string,at,commas"

31

Data.Text를 사용하면 splitOn이 있습니다.

http://hackage.haskell.org/packages/archive/text/0.11.2.0/doc/html/Data-Text.html#v:splitOn

이것은 Haskell 플랫폼에 내장되어 있습니다.

예를 들어 :

import qualified Data.Text as T
main = print $ T.splitOn (T.pack " ") (T.pack "this is a test")

또는:

{-# LANGUAGE OverloadedStrings #-}

import qualified Data.Text as T
main = print $ T.splitOn " " "this is a test"

1
@RussAbbott text패키지에 종속 되거나 설치해야 할 수도 있습니다. 그래도 다른 질문에 속할 것입니다.
Emmanuel Touzery

'Char'와 'T.Text'유형을 일치시킬 수 없습니다. 예상 유형 : [Char] 실제 유형 : [T.Text]
Andrew Koster

19

Text.Regex (Haskell 플랫폼의 일부) 모듈에는 다음과 같은 기능이 있습니다.

splitRegex :: Regex -> String -> [String]

정규식을 기반으로 문자열을 분할합니다. API는 Hackage 에서 찾을 수 있습니다 .


Could not find module ‘Text.Regex’ Perhaps you meant Text.Read (from base-4.10.1.0)
앤드류 코스터

18

를 사용 Data.List.Split하는 다음을 사용하십시오 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

이거 한번 해봐:

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

단일 문자에서만 작동하지만 쉽게 확장 가능해야합니다.


10

공백을 하나의 문자로 직접 대체하지 않고 대상 구분 기호 words는 공백입니다. 다음과 같은 것 :

words [if c == ',' then ' ' else c|c <- "my,comma,separated,list"]

또는

words let f ',' = ' '; f c = c in map f "my,comma,separated,list"

이를 매개 변수가있는 함수로 만들 수 있습니다. 다음 과 같이 일치하는 많은 문자와 일치 하는 매개 변수를 제거 할 수 있습니다 .

 [if elem c ";,.:-+@!$#?" then ' ' else c|c <-"my,comma;separated!list"]

9
split :: Eq a => a -> [a] -> [[a]]
split d [] = []
split d s = x : split d (drop 1 y) where (x,y) = span (/= d) s

예 :

split ';' "a;bb;ccc;;d"
> ["a","bb","ccc","","d"]

단일 후행 구분 기호가 삭제됩니다.

split ';' "a;bb;ccc;;d;"
> ["a","bb","ccc","","d"]

6

나는 어제 Haskell을 배우기 시작했습니다.

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)

제공합니다 :

*Main> split ' ' "this is a test"
["this","is","a","test"]

아니면 당신이 원하는

*Main> splitWithStr  " and " "this and is and a and test"
["this","is","a","test"]

다음과 같습니다.

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)

1
split잘 개발 된 라이브러리가있는 언어에 의해 손상되는 내장을 찾고있었습니다 . 어쨌든 고마워
Eric Wilson

3
당신은 6 월에 이것을 썼으므로, ​​나는 당신이 당신의 여정에서 나아 갔다고 가정합니다. 즐기세요!
Tony Morris

5

나는 스티브의 대답에 댓글을 추가하는 방법을 몰라,하지만 난 추천하고 싶습니다
  GHC 라이브러리 문서를 ,
그리고 거기에 특별히
  Data.List에서 하위 목록 기능

단순한 Haskell 보고서를 읽는 것보다 참조로 훨씬 좋습니다.

일반적으로 피드 할 새 하위 목록을 만들 때 규칙이있는 접기도 해결해야합니다.


2

답변에 주어진 효율적이고 사전 작성된 함수 외에도, 나는 내 자신의 시간에 언어를 배우기 위해 작성했던 Haskell 함수의 레퍼토리의 일부인 내 자신을 추가 할 것입니다.

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

솔루션은 적어도 꼬리 재귀이므로 스택 오버플로가 발생하지 않습니다.


2

ghci의 예 :

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

1
정규식을 사용하여 문자열을 나누지 마십시오. 감사합니다.
kirelagin

@kirelagin, 왜이 코멘트? 나는 Haskell을 배우고 있으며, 귀하의 의견 뒤에 합리적인 것을 알고 싶습니다.
Enrico Maria De Angelis

@Andrey, 내 첫 줄을 실행할 수없는 이유가 ghci있습니까?
Enrico Maria De Angelis

1
@EnricoMariaDeAngelis 정규식은 문자열 일치를위한 강력한 도구입니다. 사소하지 않은 것을 일치시킬 때 사용하는 것이 좋습니다. 문자열을 다른 고정 문자열처럼 사소한 것으로 나누려면 정규 표현식을 사용할 필요가 없습니다. 코드를 더 복잡하고 느리게 만들뿐입니다.
kirelagin

"문자열을 나누기 위해 정규식을 사용하지 마십시오." 왜 안돼 ??? 정규식으로 문자열을 분할하는 것은 매우 합리적인 일입니다. 문자열을 분리해야하지만 구분 기호가 항상 동일하지 않은 사소한 경우가 많이 있습니다.
앤드류 코스터

2

이해하기가 더 간단합니다.

split :: Char -> String -> [String]
split c xs = case break (==c) xs of 
  (ls, "") -> [ls]
  (ls, x:rs) -> ls : split c rs
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.