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

时间:2010-12-21 21:12:40

标签: string haskell split

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

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

以下是生成的程序

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

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

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

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

  1. 将分隔符添加到字符串",1,2,-5,-23,15"

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

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

  5. 删除第一个分隔符并使用符号,直到符合下一个分隔符["1", "2", .... ]

  6. 所以问题是:

    如何改进功能splitByDelimiter

    我可以删除前缀和掉落分隔符并直接拆分字符串吗?

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

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

9 个答案:

答案 0 :(得分:33)

Data.List.Split.splitOn不这样做吗?

答案 1 :(得分:23)

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

编辑:不是由原作者编写的,但下面是更多(过度?)详细且不太灵活的版本(特定于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"]

答案 2 :(得分:11)

这有点像黑客,但是,它确实有效。

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

以下是使用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

答案 3 :(得分:7)

为了好玩,以下是如何使用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

一个优点是它可以轻松创建一个灵活的解析器:

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

答案 4 :(得分:4)

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

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

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

答案 5 :(得分:2)

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 

答案 6 :(得分:1)

此代码工作正常 使用: - 拆分&#34;你的字符串&#34; []并替换&#39;,&#39;任何分隔符

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

答案 7 :(得分:1)

 A()
 {
   System.out.print("Inside A");
    B obj=new B(); // You are creating a new object of Class B
 }

答案 8 :(得分:0)

另一个没有进口的物品:

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)