简体   繁体   中英

Haskell equivalent of scala collect

I'm trying to read in a file containing key/value pairs of the form:

#A comment
a=foo
b=bar
c=baz
Some other stuff

With various other lines, as suggested. This wants to go into a map that I can then look up keys from.

My initial approach would be to read in the lines and split on the '=' character to get a [[String]] . In Scala, I would then use collect , which takes a partial function (in this case something like \\x -> case x of a :: b :: _ -> (a,b) and applies it where it's defined, throwing away the values where the function is undefined. Does Haskell have any equivalent of this?

Failing that, how would one do this in Haskell, either along my lines or using a better approach?

Typically this is done with the Maybe type and catMaybes :

catMaybes :: [Maybe a] -> [a]

So if your parsing function has type:

parse :: String -> Maybe (a,b)

then you can build the map by parsing the input string into lines, validating each line and returning just the defined values:

Map.fromList . catMaybes . map parse . lines $ s

where s is your input string.

The List Monad provides what you are looking for. This can probably be most easily leveraged via a list comprehension, although, it works with do notation as well.

First, here's the Scala implementation for reference -

// Using .toList for simpler demonstration
scala> val xs = scala.io.Source.fromFile("foo").getLines().toList
List[String] = List(a=1, b=2, sdkfjhsdf, c=3, sdfkjhsdf, d=4)

scala> xs.map(_.split('=')).collect { case Array(k, v) => (k, v) }
List[(String, String)] = List((a,1), (b,2), (c,3), (d,4))

Now the list comprehension version using Haskell -

λ :m + Data.List.Split
λ xs <- lines <$> readFile "foo"
λ xs
["a=1","b=2","sdkfjhsdf","c=3","sdfkjhsdf","d=4"]

-- List comprehension
λ [(k, v) | [k, v] <- map (splitOn "=") xs]
[("a","1"),("b","2"),("c","3"),("d","4")]

-- Do notation
λ do { [k, v] <- map (splitOn "=") xs; return (k, v) }
[("a","1"),("b","2"),("c","3"),("d","4")]

What's happening is that the pattern match condition is filtering out cases that don't match using the fail method from Monad .

λ fail "err" :: [a]
[]

So both the list comprehension and do notation are leveraging fail , which desugars to this -

map (splitOn "=") xs >>= (
  \s -> case s of
    [k, v] -> return (k, v)
    _ -> fail ""
)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM