Каков наилучший способ разделить строку с помощью разделителя функционально? - PullRequest
23 голосов
/ 22 декабря 2010

Я пытался написать программу на Хаскеле, которая будет принимать строку целых чисел, разделенных запятой, преобразовывать ее в список целых чисел и увеличивать каждое число на 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"]

Думал, что это работает, я не доволен тем, как это написано. Есть много скобок, так что это выглядит как Лисп. Также алгоритм несколько искусственный:

  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", .... ]

Итак, вопросы:

Как мне улучшить функцию splitByDelimiter?

Могу ли я удалить предизначение и падение разделителя и сделать прямое разбиение строки?

Как мне переписать функцию, чтобы было меньше скобок?

Может быть, я что-то упустил и уже есть стандартная функция с этим функционалом?

Ответы [ 8 ]

33 голосов
/ 22 декабря 2010

Разве Data.List.Split.splitOn не делает этого?

23 голосов
/ 27 сентября 2011
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"]
11 голосов
/ 22 декабря 2010

Это что-то вроде хака, но, черт возьми, это работает.

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
7 голосов
/ 25 декабря 2010

Просто для удовольствия, вот как вы можете создать простой парсер с 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 голосов
/ 22 декабря 2010

Это приложение ответа 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")

2 голосов
/ 03 марта 2013
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 
1 голос
/ 28 июня 2015
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"
1 голос
/ 30 марта 2015

Этот код отлично работает используйте: - разделите "Ваша строка" [] и замените ',' любым разделителем

split [] t = [t]
split (a:l) t = if a==',' then (t:split l []) else split l (t++[a])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...