string 在功能上用分隔符分割字符串的最佳方法是什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4503958/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 00:56:00  来源:igfitidea点击:

What is the best way to split a string by a delimiter functionally?

stringhaskellsplit

提问by sign

I tried to write the program in Haskell that will take a string of integer numbers delimitated by comma, convert it to list of integer numbers and increment each number by 1.

我尝试在 Haskell 中编写程序,该程序将接收一串由逗号分隔的整数,将其转换为整数列表,并将每个数字加 1。

For example "1,2,-5,-23,15" -> [2,3,-4,-22,16]

例如 "1,2,-5,-23,15" -> [2,3,-4,-22,16]

Below is the resulting program

下面是结果程序

import Data.List

main :: IO ()
main = do
  n <- return 1
  putStrLn . show . map (+1) . map toInt . splitByDelimiter delimiter
    $ getList n

getList :: Int -> String
getList n = foldr (++) [] . intersperse [delimiter] $ replicate n inputStr

delimiter = ','

inputStr = "1,2,-5,-23,15"

splitByDelimiter :: Char -> String -> [String]
splitByDelimiter _ "" = []
splitByDelimiter delimiter list =
  map (takeWhile (/= delimiter) . tail)
    (filter (isPrefixOf [delimiter])
       (tails
           (delimiter : list)))

toInt :: String -> Int
toInt = read

The most hard part for me was programming of function splitByDelimiterthat take a String and return list of Strings

对我来说最困难的部分是函数的编程,splitByDelimiter它接受一个字符串并返回字符串列表

"1,2,-5,-23,15" -> ["1","2","-5","-23","15"]

"1,2,-5,-23,15" -> ["1","2","-5","-23","15"]

Thought it is working, I am not happy with the way it is written. There are a lot of parentheses, so it looks Lisp like. Also the algorithm is somewhat artificial:

认为它有效,我对它的编写方式不满意。有很多括号,所以它看起来像 Lisp。该算法也有点人为:

  1. Prepend delimiter to beginning of string ",1,2,-5,-23,15"

  2. Generate list of all tails [",1,2,-5,-23,15", "1,2,-5,-23,15", ",2,-5,-23,15", .... ]

  3. Filter and left only strings that begins with delimiter [",1,2,-5,-23,15", ",2,-5,-23,15", .... ]

  4. Drop first delimiter and take symbols until next delimiter will be met ["1", "2", .... ]

  1. 在字符串开头添加分隔符 ",1,2,-5,-23,15"

  2. 生成所有尾部的列表 [",1,2,-5,-23,15", "1,2,-5,-23,15", ",2,-5,-23,15", .... ]

  3. 过滤并只留下以分隔符开头的字符串 [",1,2,-5,-23,15", ",2,-5,-23,15", .... ]

  4. 删除第一个分隔符并取符号直到遇到下一个分隔符 ["1", "2", .... ]

So the questions are:

所以问题是:

How I can improve function splitByDelimiter?

我怎样才能改善功能splitByDelimiter

Can I remove prepend and drop of delimiter and make direct split of string?

我可以删除分隔符的前置和删除并直接拆分字符串吗?

How I can rewrite the function so there will be less parentheses?

我如何重写函数以便减少括号?

May be I miss something and there are already standard function with this functionality?

可能是我错过了一些东西,并且这个功能已经有标准功能了吗?

回答by Mikel

Doesn't Data.List.Split.splitOndo this?

Data.List.Split.splitOn做到这一点?

回答by Satvik

splitBy delimiter = foldr f [[]] 
            where f c l@(x:xs) | c == delimiter = []:l
                             | otherwise = (c:x):xs

Edit: not by the original author, but below is a more (overly?) verbose, and less flexible version (specific to Char/String) to help clarify how this works. Use the above version because it works on any list of a type with an Eqinstance.

编辑:不是原作者,但下面是一个更(过度?)冗长且不太灵活的版本(特定于Char/ String),以帮助阐明它是如何工作的。使用上面的版本,因为它适用于具有Eq实例的任何类型列表。

splitBy :: Char -> String -> [String]
splitBy _ "" = [];
splitBy delimiterChar inputString = foldr f [""] inputString
  where f :: Char -> [String] -> [String]
        f currentChar allStrings@(partialString:handledStrings)
          | currentChar == delimiterChar = "":allStrings -- start a new partial string at the head of the list of all strings
          | otherwise = (currentChar:partialString):handledStrings -- add the current char to the partial string

-- input:       "a,b,c"
-- fold steps:
-- first step:  'c' -> [""] -> ["c"]
-- second step: ',' -> ["c"] -> ["","c"]
-- third step:  'b' -> ["","c"] -> ["b","c"]
-- fourth step: ',' -> ["b","c"] -> ["","b","c"]
-- fifth step:  'a' -> ["","b","c"] -> ["a","b","c"]

回答by HaskellElephant

This is a bit of a hack, but heck, it works.

这有点像黑客,但见鬼,它有效。

yourFunc str = map (+1) $ read ("[" ++ str ++ "]")

Here is a non-hack version using unfoldr:

这是一个非黑客版本,使用unfoldr

import Data.List
import Control.Arrow(second)

-- break' is like break but removes the
-- delimiter from the rest string
break' d = second (drop 1) . break d

split :: String -> Maybe (String,String)
split [] = Nothing
split xs = Just . break' (==',') $?xs

yourFunc :: String -> [Int]
yourFunc = map ((+1) . read) . unfoldr split

回答by Michael Steele

Just for fun, here is how you could create a simple parser with Parsec:

只是为了好玩,这里是如何使用 Parsec 创建一个简单的解析器:

module Main where

import Control.Applicative hiding (many)
import Text.Parsec
import Text.Parsec.String

line :: Parser [Int]
line = number `sepBy` (char ',' *> spaces)

number = read <$> many digit

One advantage is that it's easily create a parser which is flexible in what it will accept:

一个优点是它很容易创建一个解析器,它可以灵活地接受:

*Main Text.Parsec Text.Parsec.Token> :load "/home/mikste/programming/Temp.hs"
[1 of 1] Compiling Main             ( /home/mikste/programming/Temp.hs, interpreted )
Ok, modules loaded: Main.
*Main Text.Parsec Text.Parsec.Token> parse line "" "1, 2, 3"
Right [1,2,3]
*Main Text.Parsec Text.Parsec.Token> parse line "" "10,2703,   5, 3"
Right [10,2703,5,3]
*Main Text.Parsec Text.Parsec.Token> 

回答by sign

This is application of HaskellElephant's answer to original question with minor changes

这是 HaskellElephant 对原始问题的回答的应用,略有改动

splitByDelimiter :: Char -> String -> [String]
splitByDelimiter = unfoldr . splitSingle

splitSingle :: Char -> String -> Maybe (String,String)
splitSingle _ [] = Nothing
splitSingle delimiter xs =
  let (ys, zs) = break (== delimiter) xs in
  Just (ys, drop 1 zs)

Where the function splitSingle split the list in two substrings by first delimiter.

其中函数 splitSingle 通过第一个分隔符将列表拆分为两个子字符串。

For example: "1,2,-5,-23,15" -> Just ("1", "2,-5,-23,15")

例如: "1,2,-5,-23,15" -> Just ("1", "2,-5,-23,15")

回答by zurgl

splitBy del str = helper del str []   
    where 
        helper _ [] acc = let acc0 = reverse acc in [acc0] 
        helper del (x:xs) acc   
            | x==del    = let acc0 = reverse acc in acc0 : helper del xs []  
            | otherwise = let acc0 = x : acc     in helper del xs acc0 

回答by techcomp

This code works fine use:- split "Your string" [] and replace ',' with any delimiter

此代码可以正常使用:- 拆分“您的字符串”[] 并用任何分隔符替换“,”

split [] t = [t]
split (a:l) t = if a==',' then (t:split l []) else split l (t++[a])

回答by Efe Ariaroo

import qualified Text.Regex as RegExp

myRegexSplit :: String -> String -> [String]
myRegexSplit regExp theString = 
  let result = RegExp.splitRegex (RegExp.mkRegex regExp) theString
  in filter (not . null) result

-- using regex has the advantage of making it easy to use a regular
-- expression instead of only normal strings as delimiters.

-- the splitRegex function tends to return an array with an empty string
-- as the last element. So the filter takes it out

-- how to use in ghci to split a sentence
let timeParts = myRegexSplit " " "I love ponies a lot"

回答by Lucas Moeskops

Another without imports:

另一个没有进口:

splitBy :: Char -> String -> [String]
splitBy _ [] = []
splitBy c s  =
  let
    i = (length . takeWhile (/= c)) s
    (as, bs) = splitAt i s
  in as : splitBy c (if bs == [] then [] else tail bs)