Haskell'de bir dize nasıl bölünür?


163

Haskell'de bir dizeyi bölmenin standart bir yolu var mı?

linesve wordsbir boşlukta veya satırsonunda bölünmekten harika çalışıyor, ama kesinlikle bir virgül üzerinde bölünmenin standart bir yolu var mı?

Hoogle'da bulamadım.

Spesifik olarak, split "," "my,comma,separated,list"geri dönecek bir şey arıyorum ["my","comma","separated","list"].


21
Gelecekteki bir sürümünde Data.Listya da hatta böyle bir işlevi gerçekten istiyorum Prelude. Kod-golf için mevcut değilse çok yaygın ve pis.
fuz

Yanıtlar:


135

Bunun için split denilen bir paket var .

cabal install split

Şöyle kullanın:

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

Eşleşen sınırlayıcılara bölmek veya birkaç sınırlayıcıya sahip olmak için birçok başka işlevle birlikte gelir.


9
Güzel. Bu paketin farkında değildim. Bu, operasyon üzerinde çok fazla kontrol sağladığı için nihai bölünmüş pakettir (sonuçlardaki alanı kırpın, sonuçta ayırıcıları bırakın, ardışık ayırıcıları çıkarın, vb.). Listeleri bölmenin birçok yolu var, splither ihtiyaca cevap verecek tek bir işleve sahip olmak mümkün değil , gerçekten bu tür bir pakete ihtiyacınız var.
gawi

1
Aksi takdirde, harici paketler kabul edilebilirse, MissingH ayrıca bir bölünmüş fonksiyon sağlar: hackage.haskell.org/packages/archive/MissingH/1.2.0.0/doc/html/… Bu paket aynı zamanda birçok "hoş" işlev sunar ve bazı paketlerin buna bağlı olduğunu görüyorum.
Emmanuel Touzery

41
Bölünmüş paket artık en son sürümden itibaren haskell platformundan ayrı.
İnternet

14
Data.List.Split (splitOn) dosyasını içe aktarın ve şehre gidin. splitOn :: Eq a => [a] -> [a] -> [[a]]
İnternet

1
@RussAbbott bölünmüş paket indirdiğinizde Haskell Platformunda bulunur ( haskell.org/platform/contents.html ), ancak projenizi oluştururken otomatik olarak yüklenmez. Ekle splitiçin build-dependsproje merhaba denir o zaman içinde, örneğin sizin Cabal dosyasında listesinin hello.cabalaltındaki dosyada executable hellotabanı, split` (not iki uzay girintisi): Satır birikmesi bağlıdır `gibi bir satır koydu. Sonra cabal buildkomutu kullanarak derleyin . Krş haskell.org/cabal/users-guide/…
expz

164

Prelude fonksiyonlarının tanımına bakabileceğinizi unutmayın!

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

Oraya bakıldığında words,

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

Bu nedenle, yüklem alan bir işlev için değiştirin:

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

O zaman istediğiniz yüklemle çağırın!

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

31

Data.Text kullanıyorsanız splitOn var:

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

Bu Haskell Platformunda oluşturulmuştur.

Örneğin:

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

veya:

{-# LANGUAGE OverloadedStrings #-}

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

1
@RussAbbott muhtemelen textpakete bağımlı olmanız veya yüklemeniz gerekir. Yine de başka bir soruya ait olurdu.
Emmanuel Touzery

'T.Text' türü ile 'Char' eşleşemedi Beklenen tür: [Char] Gerçek tür: [T.Text]
Andrew Koster

19

Text.Regex modülünde (Haskell Platformunun bir parçası), bir işlev vardır:

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

normal bir ifadeye dayalı olarak bir dize böler. API, Hackage'da bulunabilir .


Could not find module ‘Text.Regex’ Perhaps you meant Text.Read (from base-4.10.1.0)
Andrew Koster

18

Kullanım Data.List.Splitkullanır 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

Bunu dene:

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

Sadece tek bir karakter için çalışır, ancak kolayca genişletilebilir olmalıdır.


10

Hiçbir şey bir alan için bir karakterin doğrudan yerine konulmadan, hedef ayırıcı wordsbir boşluktur. Gibi bir şey:

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

veya

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

Bunu parametrelerle bir fonksiyona dönüştürebilirsiniz. Eşleştiğim birçok karakterle eşleşecek karakter karakterini şu şekilde kaldırabilirsiniz :

 [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

Örneğin

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

Tek bir izleyen sınırlayıcı bırakılır:

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

6

Dün Haskell öğrenmeye başladım, eğer yanılıyorsam beni düzelt ama:

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)

verir:

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

ya da belki istedin

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

hangisi olurdu:

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
Yerleşik bir yer arıyordum split, iyi gelişmiş kütüphanelere sahip diller tarafından şımartıldım. Yinede teşekkürler.
Eric Wilson

3
Bunu Haziran ayında yazdınız, bu yüzden yolculuğunuzda ilerlediğinizi varsayıyorum :) Bir alıştırma olarak, bu işlevi tersine veya uzunluğa sahip olmadan yeniden yazmayı denemek, bu işlevlerin kullanımı algoritmik bir karmaşıklık cezasına neden olur ve ayrıca sonsuz bir listeye uygulamayı engeller. İyi eğlenceler!
Tony Morris


2

Cevaplarda verilen verimli ve önceden oluşturulmuş fonksiyonlara ek olarak, kendi zamanımda dili öğrenmek için yazdığım Haskell fonksiyonlarının repertuarının bir parçası olan kendim ekleyeceğim:

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

Çözümler en azından kuyruk yinelemelidir, bu nedenle yığın taşmasına neden olmazlar.


2

Ghci'de örnek:

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

1
Lütfen, dizeleri bölmek için normal ifadeler kullanmayın. Teşekkür ederim.
kirelagin

@kirelagin, neden bu yorum? Haskell öğreniyorum ve yorumunuzun ardındaki mantığı bilmek istiyorum.
Enrico Maria De Angelis

@Andrey, ilk satırı bile çalıştıramamamın bir nedeni var ghcimı?
Enrico Maria De Angelis

1
@EnricoMariaDeAngelis Normal ifadeler dize eşleşmesi için güçlü bir araçtır. Önemsiz bir şeyle eşleştirdiğinizde bunları kullanmak mantıklıdır. Bir dizeyi başka bir sabit dize kadar önemsiz bir şeye bölmek istiyorsanız, düzenli ifadeler kullanmaya kesinlikle gerek yoktur - kodu yalnızca daha karmaşık ve muhtemelen daha yavaş hale getirecektir.
kirelagin

"Lütfen, dizeleri ayırmak için normal ifadeler kullanmayın." WTF, neden olmasın ??? Bir dizeyi normal bir ifadeyle bölmek, yapılması oldukça makul bir şeydir. Bir dizenin bölünmesi gereken birçok önemsiz durum vardır, ancak sınırlayıcı her zaman tam olarak aynı değildir.
Andrew Koster

2

Bunu anlamak daha basit buluyorum:

split :: Char -> String -> [String]
split c xs = case break (==c) xs of 
  (ls, "") -> [ls]
  (ls, x:rs) -> ls : split c rs
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.