วิธีการแยกสตริงใน Haskell หรือไม่?


163

มีวิธีมาตรฐานในการแบ่งสตริงใน Haskell หรือไม่?

linesและwordsที่ดีทำงานจากที่แยกในพื้นที่หรือขึ้นบรรทัดใหม่ แต่ก็มีวิธีมาตรฐานในการแยกจุลภาคหรือไม่?

ฉันหามันไม่พบใน Hoogle

จะเฉพาะเจาะจง, ฉันกำลังมองหาบางสิ่งบางอย่างที่ผลตอบแทนsplit "," "my,comma,separated,list"["my","comma","separated","list"]


21
ผมต้องการไปเช่นฟังก์ชั่นในรุ่นอนาคตของหรือแม้กระทั่งData.List Preludeเป็นเรื่องปกติและน่ารังเกียจมากหากไม่มีรหัสสำหรับกอล์ฟ
fuz

คำตอบ:


135

มีแพ็คเกจสำหรับการแยกที่เรียกว่านี้

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/ ......แพคเกจนั้นยังมีฟังก์ชั่น "ที่ดีที่จะมี" มากมาย และฉันพบว่าบางแพ็คเกจขึ้นอยู่กับมัน
เอ็มมานู Touzery

41
แพ็คเกจแบบแยกเป็นส่วนหนึ่งของแพลตฟอร์ม Haskell ซึ่งเป็นรุ่นล่าสุด
อินเทอร์เน็ต

14
นำเข้า Data.List.Split (splitOn) และไปที่เมือง splitOn :: สม A => [เป็น] -> [เป็น] -> [[เป็น]]
อินเทอร์เน็ต

1
@RussAbbott แพ็คเกจแยกจะรวมอยู่ในแพลตฟอร์ม Haskell เมื่อคุณดาวน์โหลด ( haskell.org/platform/contents.html ) แต่จะไม่โหลดโดยอัตโนมัติเมื่อสร้างโครงการของคุณ เพิ่มsplitลงในbuild-dependsรายการในไฟล์ cabal ของคุณเช่นหากโครงการของคุณเรียกว่า hello จากนั้นในhello.cabalไฟล์ด้านล่างexecutable helloบรรทัดให้ใส่บรรทัดเช่น "build-dependent: base, split" (หมายเหตุการเว้นวรรคสองช่อง) แล้วสร้างโดยใช้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

นี้ถูกสร้างขึ้นใน Platform 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

ไม่สามารถจับคู่ประเภท '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)
แอนดรู 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

หากไม่มีการนำเข้าสิ่งใดก็ตามที่เป็นการทดแทนโดยตรงของอักขระหนึ่งตัวสำหรับช่องว่างตัวคั่นเป้าหมายสำหรับ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
คุณเขียนสิ่งนี้ในเดือนมิถุนายนดังนั้นฉันคิดว่าคุณได้เดินหน้าต่อไปในการเดินทางของคุณ :) ในแบบฝึกหัดลองเขียนฟังก์ชั่นนี้ใหม่โดยไม่ต้องย้อนกลับหรือยาวเมื่อใช้งานฟังก์ชั่นเหล่านี้ มีความสุข!
โทนี่มอร์ริส

5

ผมไม่ทราบว่าวิธีการที่จะเพิ่มความคิดเห็นลงในคำตอบของสตีฟ แต่ผมอยากจะแนะนำ
  เอกสาร GHC ห้องสมุด ,
และมีเฉพาะ
  ฟังก์ชั่นในรายการย่อย Data.List

ซึ่งเป็นการอ้างอิงที่ดีกว่าการอ่านรายงาน Haskell ธรรมดา

โดยทั่วไปพับด้วยกฎเมื่อสร้างรายการย่อยใหม่เพื่อฟีดควรแก้ไขด้วย


2

นอกเหนือจากฟังก์ชั่นที่มีประสิทธิภาพและสร้างขึ้นล่วงหน้าที่ให้ไว้ในคำตอบฉันจะเพิ่มของฉันเองซึ่งเป็นเพียงส่วนหนึ่งของ repertory ของฟังก์ชั่น 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

"โปรดอย่าใช้นิพจน์ทั่วไปเพื่อแยกสตริง" 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.