Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

haskell [[Char]] to [[Int]]

I've got problem with a Haskell program. I'm trying to change [[Char]] to [[Int]] I've got

["2","2","1","2,2","1"] 

list of char list and I'm trying to change it to [[Int]]

[[2],[2],[1],[2,2],[1]]

I've tried

f :: [String] -> [Int]
f = map read

but it gives me

[2,2,1,*** Exception: Prelude.read: no parse

Can anybody help me with this?

like image 505
alekq Avatar asked May 13 '19 16:05

alekq


People also ask

How do you convert char to Int in Haskell?

A character literal in Haskell has type Char . To convert a Char to or from the corresponding Int value defined by Unicode, use toEnum and fromEnum from the Enum class respectively (or equivalently ord and chr ).

What is data char in Haskell?

The character type Char is an enumeration whose values represent Unicode (or equivalently ISO 10646) characters. This set extends the ISO 8859-1 (Latin-1) character set (the first 256 charachers), which is itself an extension of the ASCII character set (the first 128 characters).

How do I find the char of a number?

If the char variable contains an int value, we can get the int value by calling Character. getNumericValue(char) method. Alternatively, we can use String. valueOf(char) method.


1 Answers

The reason that this fails is because a string "2,2" can not be converted to an Int itself: this is a digit followed by a comma, followed by a digit. An Int is parsed by an optional minus sign, followed by some digits, and some extra possibilities like hexadecimal numbers, but let us ignore these for now.

The type signature you specify for f is however incorrect, based on the expected output. Your output type seems to be a list of lists of Ints, so [[Int]]. That means that you should specify f as:

f :: [String] -> [[Int]]
f = ...

We thus need to read every String to an [Int]. We can not use read directly here, since reading to an [Int] expects the string to start and end with square brackets. We can however add these manually like:

f :: [String] -> [[Int]]
f = map (\s -> read ('[' : s ++ "]"))

or a point-free version:

f :: [String] -> [[Int]]
f = map (read . ('[' :) . (++ "]"))

For example:

Prelude> f ["2","2","1","2,2","1"] 
[[2],[2],[1],[2,2],[1]]

Towards safer parsing with readMaybe

Parsing from Strings like in the above way is of course not very "safe", since it is possible that the String does not follow the format. We can make this more safe and use for example readMaybe :: Read a => String -> Maybe a:

import Text.Read(readMaybe)

f :: [String] -> [Maybe [Int]]
f = map (readMaybe . ('[' :) . (++ "]"))

For example:

Prelude Text.Read> f ["2", "3;2", "4,7,3", "bla"]
[Just [2],Nothing,Just [4,7,3],Nothing]

we can omit the failed reads for example by using catMaybes :: [Maybe a] -> [a]:

import Data.Maybe(catMaybes)
import Text.Read(readMaybe)

f :: [String] -> [[Int]]
f = catMaybes . map (readMaybe . ('[' :) . (++ "]"))

For example:

Prelude Data.Maybe Text.Read> f ["2", "3;2", "4,7,3", "bla"]
[[2],[4,7,3]]

or as @dfeuer said, we can use traverse :: (Applicative f, Traversable t) => (a -> f b) -> t a -> f (t b) to return an [[Int]] result wrapped in a Just if all parsing succeeded, and Nothing otherwise:

import Text.Read(readMaybe)

f :: [String] -> Maybe [[Int]]
f = traverse (readMaybe . ('[' :) . (++ "]"))

For example:

Prelude Text.Read> f ["2","2","1","2,2","1"] 
Just [[2],[2],[1],[2,2],[1]]
Prelude Text.Read> f ["2", "3;2", "4,7,3", "bla"]
Nothing

Parse with error messages with readEither

We can obtain an error message wrapped in a Left in case the parsing fails by using readEither :: Read a => String -> Either String a:

import Text.Read(readEither)

f :: [String] -> [Either String [Int]]
f = map (readEither . ('[' :) . (++ "]"))

For example:

Prelude Text.Read> f ["2", "3;2", "4,7,3", "bla"]
[Right [2],Left "Prelude.read: no parse",Right [4,7,3],Left "Prelude.read: no parse"]

and use traverse in the same way to obtain an error message wrapped in a Left or the complete result in a Right:

import Text.Read(readEither)

f :: [String] -> Either String [[Int]]
f = traverse (readEither . ('[' :) . (++ "]"))

For example:

Prelude Text.Read> f ["2","2","1","2,2","1"] 
Right [[2],[2],[1],[2,2],[1]]
Prelude Text.Read> f ["2", "3;2", "4,7,3", "bla"]
Left "Prelude.read: no parse"

Here, like @dfeuer says, it does not really shows much information. There are however parsers that can provide more informative parsing errors.

like image 71
Willem Van Onsem Avatar answered Sep 21 '22 05:09

Willem Van Onsem