2010-03-18 71 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"],我使用Haskell的words函數分解。將字符串轉換爲整數/浮點在Haskell?

我收到以下錯誤,我認爲這是因爲makeGroceryItem接受FloatInt

*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* 
分別

但我怎麼做bFloatcInt,?

+0

你有一個有趣的項目。它是爲了什麼? – 2015-12-09 14:39:46

回答

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或傳遞到期望一個字符串的任何函數。

相反,你應該使用

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 2012-06-30 13:39:44

+0

@Nawaz:是'read'是一個函數。 'f :: T'表達式強制'f'具有類型'T'。 – kennytm 2012-06-30 15:36:57

+0

@KennyTM:所以語法'read「123.456」:: Float'大致相當於'sscanf(「123.456」,「%f」,&fnum);'在C中,對嗎? – Nawaz 2012-06-30 15:55:59

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'調用? – 2015-10-11 08:12:00

+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 2016-12-29 11:06:16

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

那我怎麼在我的項目解決了這個任務。