Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cutting specific chunks from a Haskell String

Tags:

haskell

I'm trying to cut chunks from a list, with a given predicate. I would have preferred to use a double character, e.g. ~/, but have resolved to just using $. What I essentially want to do is this...

A: "Hello, my $name is$ Danny and I $like$ Haskell"

What I want to turn this into is this:

B: "Hello, my Danny and I Haskell"

So I want to strip everything in between the given symbol, $, or my first preference was ~/, if I can figure it out. What I tried was this:

s1 :: String -> String
s1 xs = takeWhile (/= '$') xs

s2 :: String -> String
s2 xs = dropWhile (/= '$') xs

s3 :: String -> String
s3 xs = s3 $ s2 $ s1 xs

This solution seems to just bug my IDE out (possibly infinite looping).

Solution:

s3 :: String -> String
s3 xs
 |'$' `notElem` xs = xs
 |otherwise = takeWhile (/= '$') xs ++ (s3 $ s1 xs)

s1 :: String -> String
s1 xs = drop 1 $ dropWhile (/= '$') $ tail $ snd $ break ('$'==) xs
like image 383
Danny Wilson Avatar asked Mar 18 '26 06:03

Danny Wilson


1 Answers

This seems like a nice application for parsers. A solution using trifecta:

import Control.Applicative
import Data.Foldable
import Data.Functor
import Text.Trifecta

input :: String
input = "Hello, my $name is$ Danny and I $like$ Haskell"

cutChunk :: CharParsing f => f String
cutChunk = "" <$ (char '$' *> many (notChar '$') <* char '$')

cutChunk matches $, followed by 0 or more (many) non-$ characters, then another $. Then we use ("" <$) to make this parser's value always be the empty string, thus discarding all the characters that this parser matches.


includeChunk :: CharParsing f => f String
includeChunk = some (notChar '$')

includeChunk matches the text that we want to include in the result, which is anything that's not the $ character. It's important that we use some (matching one or more characters) and not many (matching zero or more characters) because we're going to include this parser within another many expression next; if this parser matched on the empty string, then that could loop infinitely.


chunks :: CharParsing f => f String
chunks = fold <$> many (cutChunk <|> includeChunk)

chunks is the parser for everything. Read <|> as "or", as in "parse either a cutChunk or an includeChunk". many (cutChunk <|> includeChunk) is a parser that produces a list of chunks e.g. Success ["Hello, my ",""," Danny and I ",""," Haskell"], so we fold the output to concatenate those chunks together into a single string.


result :: Result String
result = parseString chunks mempty input

The result:

Success "Hello, my  Danny and I  Haskell"
like image 89
Chris Martin Avatar answered Mar 20 '26 09:03

Chris Martin



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!