Haskellで文字列を分割する方法は?


163

Haskellで文字列を分割する標準的な方法はありますか?

linesそしてwordsスペースや改行で分割からの仕事の偉大な、しかし確実に、カンマで分割するための標準的な方法はありますか?

Hoogleでは見つかりませんでした。

具体的には、split "," "my,comma,separated,list"戻ってくるところを探しています["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あります。すべてのニーズに応える単一の機能を持つことはできません。そのようなパッケージが本当に必要です。
ガウイ

1
外部パッケージが許容されているそれ以外の場合は、MissingHもスプリット機能を提供します:hackage.haskell.org/packages/archive/MissingH/1.2.0.0/doc/html/...パッケージは、他の「素敵なツー持っている」の機能の多くを提供することそして、かなりのパッケージがそれに依存していることがわかりました。
Emmanuel Touzery

41
最新のリリースでは、splitパッケージはhaskellプラットフォームの一部になりました。
インターネット

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」のような行を挿入します(2つのスペースインデントに注意)。次に、cabal buildコマンドを使用してビルドします。Cf. haskell.org/cabal/users-guide/...
expz

164

Prelude関数の定義を検索できることを忘れないでください!

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

そこを見ると、の定義wordsは、

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 2016年

タイプ「T.Text」を「Char」と一致させることができませんでした予想されるタイプ:[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)
Andrew Koster

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

スペースを1文字で直接置換したものを何もインポートしない場合、対象となるセパレーター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、よく開発されたライブラリーを備えた言語に甘やかされている組み込みのを探していました。とにかくありがとう。
エリックウィルソン

3
あなたは6月にこれを書いたので、あなたはあなたの旅に移ったと思います:)演習として、これらの関数を使用するとアルゴリズムの複雑さのペナルティが発生し、無限リストへの適用も防止されるため、逆または長さなしでこの関数を書き直そうとします。楽しんで!
トニーモリス、

5

Steveの回答にコメントを追加する方法はわかりませんが、
  GHCライブラリのドキュメント
特に
  Data.ListのSublist関数をお勧めします

これは、単純な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 2018年

@kirelagin、なぜこのコメント?私はHaskellを学んでいます、そしてあなたのコメントの背後にある理性を知りたいのです。
エンリコマリアデアンジェリス

@アンドレイ、最初の行も実行できない理由はありghciますか?
エンリコマリアデアンジェリス

1
@EnricoMariaDeAngelis正規表現は、文字列照合のための強力なツールです。重要なものを照合するときにこれらを使用することは理にかなっています。文字列を別の固定文字列と同じくらい簡単なものに分割したい場合は、正規表現を使用する必要はまったくありません。コードが複雑になり、遅くなるだけです。
キレラギン

「ストリングを分割するために正規表現を使用しないでください。」WTF、どうして??? 正規表現で文字列を分割することは、完全に合理的なことです。文字列を分割する必要がある些細なケースがたくさんありますが、区切り文字は常に正確に同じであるとは限りません。
Andrew Koster

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.