2010-03-18 4 views
63
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 Haskell.Преобразование String в Integer/Float в Haskell?

Я получаю следующее сообщение об ошибке, которое я думаю, потому что 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, соответственно?

+0

у вас есть интересный проект. для чего это? –

ответ

80

read можно разобрать строку в поплавок и 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 или передано в любые функции, ожидающие String.

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

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) 
+0

@KennyTM: 'read '123.456" :: Float'. Что означает этот синтаксис? Что такое '::' здесь? Функция 'read' является функцией? – Nawaz

+0

@Nawaz: Да 'read' является функцией. Выражение 'f :: T' заставляет' f' иметь тип 'T'. – kennytm

+0

@KennyTM: Таким образом, синтаксис 'read '123.456" :: Float' примерно эквивалентен 'sscanf (" 123.456 ","% f ", &fnum);' in C, right? – Nawaz

5

Две вещи:

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 параметра.

75

Даже если у этого вопроса уже есть ответ, я настоятельно рекомендую использовать 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 возвращает список только с одним элементом: Кортеж, состоящий из преобразованного значения и может быть unconvertable дополнительных символов. При ошибке reads возвращает пустой список.

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

+1

Отличное предложение! Каков ваш предпочтительный способ извлечения результирующего элемента из списка, возвращаемого из чтения? Два 'fst' звонка? –

+7

Начиная с базы 4.6, существует ['readMaybe :: Read a => String -> Maybe a'] (http://hackage.haskell.org/package/base-4.9.0.0/docs/Text-Read.html #v: readMaybe) в 'Text.Read', что более удобно, чем использование' reads' в этом случае. – sjakobi

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

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