Преобразовать строку в целое число /число с плавающей точкой в ​​Haskell?

data GroceryItem = CartItem ItemName Price Quantity | StockItem ItemName Price Quantity

makeGroceryItem :: String -> Float -> Int -> GroceryItem
makeGroceryItem name price quantity = CartItem name price quantity

I want to create a `GroceryItem` when using a `String` or `[String]`

createGroceryItem :: [String] -> GroceryItem
createGroceryItem (a:b:c) = makeGroceryItem a b c

Входные данные будут в формате ["Apple","15.00","5"], который я разбил, используя words function.

Я получаю следующую ошибку, потому что makeGroceryItem принимает Float и Int.

*Type error in application
*** Expression     : makeGroceryItem a read b read c
*** Term           : makeGroceryItem
*** Type           : String -> Float -> Int -> GroceryItem
*** Does not match : a -> b -> c -> d -> e -> f*

Но как мне сделать b и c типа Float и Int соответственно?

66 голосов | спросил Ranhiru Cooray 18 MaramThu, 18 Mar 2010 11:43:49 +03002010-03-18T11:43:49+03:0011 2010, 11:43:49

4 ответа


0

read может разобрать строку в float и int:

Prelude> :set +t
Prelude> read "123.456" :: Float
123.456
it :: Float
Prelude> read "123456" :: Int
123456
it :: Int

Но проблема (1) в вашем шаблоне:

createGroceryItem (a:b:c) = ...

Здесь : - это бинарный (справа ассоциативный) оператор, который добавляет элемент в список. RHS элемента должен быть списком. Поэтому, учитывая выражение a:b:c, Haskell выведет следующие типы:

a :: String
b :: String
c :: [String]

то есть. c будет рассматриваться как список строк. Очевидно, он не может быть read или передаваться в любые функции, ожидающие строку.

Вместо этого вы должны использовать

createGroceryItem [a, b, c] = ...

если в списке должно быть ровно 3 элемента или

createGroceryItem (a:b:c:xs) = ...

если ≥3 пунктов приемлемо.

Также (2) выражение

makeGroceryItem a read b read c

будет интерпретироваться как makeGroceryItem с 5 аргументами, 2 из которых являются read функция. Вам нужно использовать круглые скобки:

makeGroceryItem a (read b) (read c)
ответил kennytm 18 MaramThu, 18 Mar 2010 11:53:38 +03002010-03-18T11:53:38+03:0011 2010, 11:53:38
0

Несмотря на то, что на этот вопрос уже есть ответ, я настоятельно рекомендую использовать reads для преобразования строк, потому что это намного безопаснее, так как не сбой с неисправимым исключением.

reads :: (Read a) => String -> [(a, String)]

Prelude> reads "5" :: [(Double, String)]
[(5.0,"")]
Prelude> reads "5ds" :: [(Double, String)]
[(5.0,"ds")]
Prelude> reads "dffd" :: [(Double, String)]
[]

В случае успеха reads возвращает список с ровно одним элементом: кортеж, состоящий из преобразованного значения и, возможно, необратимых дополнительных символов. В случае ошибки reads возвращает пустой список.

Это легко сопоставить по шаблону в случае успеха и неудачи, и это не взорвется вам в лицо!

ответил LukeN 21 MarpmSun, 21 Mar 2010 23:10:26 +03002010-03-21T23:10:26+03:0011 2010, 23:10:26
0

Две вещи:

createGroceryItem [a, b, c] = makeGroceryItem a (parse b) (parse c)
-- pattern match error if not exactly 3 items in list

или альтернативно

createGroceryItem (a : b : c : _) = makeGroceryItem a (parse b) (parse c)
-- pattern match error if fewer than 3 items in list, ignore excess items

потому что : не совпадает с ++

Между тем с правой стороны - стороны, которая выдает сообщение об ошибке, которое вы видите - вы должны сгруппировать выражения, используя скобки. В противном случае parse интерпретируется как значение, которое вы хотите передать makeGroceryItem, поэтому компилятор жалуется, когда вы пытаетесь передать 5 аргументов функции, которая принимает только 3 параметра.

ответил dave4420 18 MarpmThu, 18 Mar 2010 12:01:54 +03002010-03-18T12:01:54+03:0012 2010, 12:01:54
0
filterNumberFromString :: String -> String
filterNumberFromString s =
    let allowedString = ['0'..'9'] ++ ['.', ',']
        toPoint n
            | n == ',' = '.'
            | otherwise = n

        f = filter (`elem` allowedString) s
        d = map toPoint f
    in d


convertStringToFloat :: String -> Float
convertStringToFloat s =
    let betterString = filterNumberFromString s
        asFloat = read betterString :: Float
    in asFloat

print (convertStringToFloat "15,00" + 1)

- > печатает 16,0

Вот как я решил эту задачу в своем проекте.

ответил chrisheyn 9 AM000000110000003331 2017, 11:42:33

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132