haskell [[Char]] до [[Int]] - PullRequest
       31

haskell [[Char]] до [[Int]]

4 голосов
/ 13 мая 2019

У меня проблема с программой на Haskell.Я пытаюсь изменить [[Char]] на [[Int]] У меня есть

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

список списков символов, и я пытаюсь изменить его на [[Int]]

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

Я пытался

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

, но это дает мне

[2,2,1, *** Исключение: Prelude.read: без разбора

Кто-нибудь может мне помочь с этим?

1 Ответ

5 голосов
/ 13 мая 2019

Причина этого заключается в том, что строка "2,2" не может быть преобразована в Int: это цифра, за которой следует запятая, за которой следует цифра.Int анализируется необязательным знаком минус, за которым следуют некоторые цифры и некоторые дополнительные возможности, такие как шестнадцатеричные числа, но пока давайте их игнорировать.

Однако указанная вами подпись типа для fневерно, исходя из ожидаемого результата.Ваш тип вывода выглядит как список списков из Int с, поэтому [[Int]].Это означает, что вы должны указать f как:

f :: [String] -> <b>[[Int]]</b>
f = ...

Таким образом, нам нужно прочитать каждый String в [Int].Мы не можем использовать read непосредственно здесь, так как read при [Int] ожидает, что строка начинается и заканчивается квадратными скобками .Однако мы можем добавить их вручную, например:

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

или без очков версия:

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

Например:

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

В направлении безопаснее парсинга с readMaybe

парсинг от String с, как описано выше, конечно, не очень " безопасно ", так как это возможночто String не соответствует формату.Мы можем сделать это более безопасным и использовать, например, readMaybe :: Read a => String -> Maybe a:

import Text.Read(<b>readMaybe</b>)

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

Например:

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

мы можем опуститьнапример, ошибочные чтения с использованием catMaybes :: [Maybe a] -> [a]:

import Data.Maybe(<b>catMaybes</b>)
import Text.Read(readMaybe)

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

Например:

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

или как @dfeuer сказал, что мы можем использовать traverse :: (Applicative f, Traversable t) => (a -> f b) -> t a -> f (t b), чтобы вернуть [[Int]] результат, заключенный в Just, если все синтаксический анализ завершился успешно, и Nothing в противном случае:

import Text.Read(readMaybe)

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

Например:

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

Анализировать с сообщениями об ошибках с помощью readEither

Мы можем получить сообщение об ошибке, заключенное в Left в случае сбоя синтаксического анализа с использованием readEither :: Read a => String -> Either String a:

import Text.Read(<b>readEither</b>)

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

Например:

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

и использованием traverseтаким же образом, чтобы получить сообщение об ошибке, заключенное в Left или полный результат в Right:

import Text.Read(readEither)

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

Например:

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"

Здесь, как @Dfeuer говорит, что на самом деле не показывает много информации.Однако существуют парсеры, которые могут предоставить более информативные ошибки синтаксического анализа.

...