2011-03-05 29 views
6

首先對於做'我從哪裏開始'的典型事情感到抱歉,但我完全失去了。如何將數據從IO讀取到數據結構中,然後處理數據結構?

我一直在閱讀'學習你一個偉大的偉大的Haskell'網站,現在感覺像一個時代(幾乎半個學期,我即將完成'輸入和輸出'一章,並且我仍然不知道如何編寫多行程序

我已經看到了do語句,並且你只能用它來將IO動作連接成一個單一的函數,但是我看不到我是怎麼做到的,米要去寫一個實際應用。

有人點我在正確的方向。

我來自一個C的背景是,基本上我使用HA我在這個學期的單元中使用了一個模塊,我想比較C++和Haskell(在許多方面)。我期望創建一系列的搜索和排序程序,以便我能評論他們在各自的語言和他們的速度方面的容易程度。

但是,我真的開始放棄使用Haskell六週的信心,而且我仍然不知道如何編寫完整的應用程序,而且我正在閱讀的網站中的章節似乎是越來越長。

我基本上需要創建一個將存儲在結構中的基本對象(我知道該怎麼做),更多的是我正在努力的是如何創建一個從某些文本中讀取數據的程序文件,並首先使用該數據填充結構,然後繼續處理它。作爲哈斯克爾似乎分裂IO等操作,它不會只是讓我寫一個程序多行,我正在尋找這樣的事情:

main = data <- getContent 
     let allLines = lines data 
     let myStructure = generateStruct allLines 
     sort/search/etc 
     print myStructure 

我怎麼去呢?任何好的教程,這將幫助我走出現實的方案?

-A

+0

強制性鏈接:http://www.haskell.org/tutorial/以防萬一這是繞過 - 「溫和」的當然是相對的。 – 2011-03-05 17:43:02

+0

我也學習了一個偉大的哈斯克爾,雖然只有幾天。 – Orbit 2011-03-05 17:45:51

+0

除了需要使用'do'符號或者刪除賦值('<--')並移動到使用bind('>> =')之外,您不能將'data'命名爲保留字。 – 2011-03-05 19:19:27

回答

10

你提到看到do符號,現在是時候學習如何使用do。考慮你的例子mainIO,你應該用做語法或結合:

main = do 
    dat <- getContent 
    let allLines = lines dat 
     myStructure = generateStruct allLines 
     sorted = mySort myStructure 
     searchResult = mySearch myStructure 
    print myStructure 
    print sorted 
    print searchResult 

所以,現在你有一個主要的是得到stdin,通過lines它變成[String],想必其解析爲一個結構和運行排序並搜索該結構。注意有趣的代碼是純粹的 - mySort,mySearchgenerateStruct不需要是IO(也不能是,在一個let綁定中),所以你實際上正確地使用了純粹有效的代碼。

我建議你看看如何綁定工程(>>=)和如何做符號desugars進入綁定。 This SO question應該有所幫助。

+0

注意,從學術的角度來看,Haskell中的IO都是純粹的 - 唯一的副作用是非終止。然而,將「純粹/有效」稱爲「非一元/一元」的同義詞是很常見的。爲了讓新手學習純度更加困難,有一個名爲'pure'的函數。 – nponeccop 2011-11-01 20:32:25

1

如果是從stdin閱讀和結果寫入stdout,沒有進一步intevening用戶輸入一個問題 - 作爲你的getContents提建議 - 那麼古interact :: (String -> String) -> IO(),或其他幾個版本,例如Data.ByteString.interact :: (ByteString -> ByteString) -> IO()Data.Text.interact :: (Text -> Text) -> IO()都是需要的。interact基本上是「做一個小的UNIX工具實現此項功能的」功能 - 它映射權類型爲可執行的行動功能的所有哈斯克爾教程應該提到其在第三或(類型IO()的即值)。第四頁,附有編譯指示。

所以,如果你寫

main = interact arthur 

arthur :: String -> String 
arthur = reverse 

ghc --make -O2 Reverse.hs -o reverse那麼無論你管./reverse將被理解爲字符的列表,並冒出逆轉編譯。同樣,無論你管到

main = interact (unlines . meredith . lines) 

meredith :: [String] -> [String] 
meredith = filter (not.null) 

會出現與省略空行。更有趣的是,

main = interact (unlines . map show . luther . map read . lines) 

luther :: [Int] -> [Int] 
luther = filter even 

將就換行符分隔字符流,讀它們作爲Int S,去除奇數的,併產生了適當濾波流。

main = interact (unlines . map show . emma . map read . lines) 

emma :: [Int] -> Int 
emma = sum . map square 
    where square x = x * x 

將打印換行符數字的平方和。

在這最後兩種情況下,lutheremma內部的'數據結構'是[Int],這是非常沉悶的,當然,應用於它的函數很簡單。主要的一點是讓interact的其中一種形式來處理所有的IO,從而得到像'填充結構'和'處理它'這樣的圖像。要使用interact,您需要使用合成來完成某種功能。但即使在這裏,就像第一個例子arthur:: String -> String一樣,你正在定義一個更真實的功能,更像數學意義上的東西。在類型StringByteString值只是那些在BoolInt純。

在這個基本interact類型的更復雜的情況,你的任務是這樣,首先,想怎麼你會側重於功能的期望值可以被映射到String值(在這裏,它只是show對於Int或unlines . map show對於[Int])。 interact知道該用什麼「做」字符串。 - 然後找出如何從字符串或字節串(其將包含的「原始」數據)中的主函數採用作爲參數的類型或類型定義純映射到值。在這裏,我只是使用map read . lines導致[Int]。如果你正在做一些更復雜的工作,比如樹形結構,你需要一個從[Int]MyTree Int的函數。當然,放在這個位置上的更復雜的功能應該是解析器。

然後你就可以去鎮,在這種情況下:我們實在沒有理由認爲自己是「編程」,「填充」和「加工」的。這就是LYAH的所有酷炫設備的用武之地。您的責任在於定義在特定定義範疇內的映射。在後兩種情況下,這些是從[Int][Int]和從[Int]Int,但這裏是從excellent, still incomplete, tutorialsuper-excellent Vector package衍生的類似的例子,其中一個是處理的初始數值結構是Vector Int

{-# LANGUAGE BangPatterns #-} 
import qualified Data.ByteString.Lazy.Char8  as L 
import qualified Data.Vector.Unboxed   as U 
import System.Environment 

main = L.interact (L.pack . (++"\n") . show . roman . parse) 
    where 
    parse :: L.ByteString -> U.Vector Int 
    parse bytestr = U.unfoldr step bytestr 
    step !s = case L.readInt s of 
     Nothing  -> Nothing 
     Just (!k, !t) -> Just (k, L.tail t) 

-- now the IO and stringy nonsense is out of the way 
-- so we can calculate properly: 

roman :: U.Vector Int -> Int 
roman = U.sum 

這裏再次roman是虛無,,的,從Ints矢量到Int的任何功能,無論複雜,都可以代替它。寫一個更好的roman永遠不會是「填充」「多行編程」,「處理」等問題,雖然我們當然這樣說;它只是一個通過Data.Vector和其他地方的函數組合定義真正函數的問題。天空是極限,請查看教程。

4

我會試着從一個簡單的例子開始。假設這是我們想要做的:

  1. 打開一個文件,其中包含一個整數列表並返回它。
  2. 排序此列表
  3. 我們也扭轉名單
  4. 打印結果屏幕上

讓我們也說,我們有這些功能,我們可以使用:

getContent :: IO [Int] 
sort :: [Int] -> [Int] 
reverse :: [Int] -> [Int] 
show :: a -> String 
putStrLn :: String -> IO() 

剛所以我們很清楚,我會對這些功能說一句話:

  • getContent:我編了這個函數,但是如果有這個函數就是它的簽名(你可以使用getContent = return [3,7,2,1]進行測試)。我相信你之前已經看到過這樣的簽名,至少隱約瞭解到,因爲它的確簽名不能只是getContent :: [Int]
  • sort:這是一個在Data.List模塊中定義的函數,使用簡單:sort [3,1,2]回報[1,2,3]
  • reverse:在Data.List模塊中還定義:reverse [1,3,2]回報[2,3,1]
  • show:不需要輸入任何東西,只需使用它:show 11返回字符串"11"; show [1,2,3]返回字符串"[1,2,3]"
  • putStrLn:需要一個字符串,把它放到屏幕上,並返回IO(),現在又說,因爲它的IO其簽名不能只是putStrLn :: Stiring ->()

好的,現在我們有了我們需要的所有功能來創建我們的程序,現在的問題是關於將這些功能連接在一起。讓我們開始與連接功能:

getContent :: IO [Int]sort :: [Int] -> [Int]

我認爲,如果你得到這個角色,你會很容易得到休息爲好。所以,問題是因爲getContent返回IO [Int]而不僅僅是[Int],你不能忽略或擺脫IO部分並將其推入sort。也就是說,這是你不能做連接這些功能是什麼:

sort (getRidOfIO getContent)

這裏就是>>= :: m a -> (a -> m b) -> m b操作就派上用場了。現在請注意mab類型變量因此,如果我們爲[Int]b[Int]替代mIOa,我們得到的signagure:

>>= :: IO [Int] -> ([Int] -> IO [Int]) -> IO [Int]

那些getContent再看一看和sort職能和他們的簽名,並試圖思考他們將如何適應>>=。我相信你會注意到你可以直接使用getContent作爲>>=的第一個參數。到目前爲止,>>=將要做的是將[Int]取出getContent,並將其推入作爲第二個參數提供的函數中。但是第二個參數的功能是什麼?我們不能直接使用sort :: [Int] -> [Int],我們可以嘗試下最好的事情是

\listOfInts -> sort listOfInts

,但仍然有簽名[Int] -> [Int],這樣並沒有太大幫助。這裏是另一個英雄來到劇本的地方,

return :: a -> m a

再次,am是類型變量,可以替代他們,我們會盡快

return :: [Int] -> IO [Int]

因此增加\listOfInts -> sort listOfIntsreturn在一起,我們將得到:

\listOfInts -> return $ sort listOfInts :: [Int] -> IO [Int]

哪個正是我們想要作爲>>=的第二個參數。所以讓我們用我們的膠水一起finaly連接getContentsort

getContent >>= (\listOfInts -> return $ sort listOfInts)

這是同樣的事情(使用do符號):

do listOfInts <- getContent 
    return $ sort listOfInts 

那裏,那是最末尾可怕的部分。現在可能是aha時刻之一,試着想想我們剛剛組成的連接的結果類型是什麼。我會爲你破壞它,......

getContent >>= (\listOfInts -> return $ sort listOfInts)IO [Int]的類型。讓我們總結一下:我們採取了IO [Int]類型的東西和[Int] -> [Int]類型的東西,將這兩件事粘在一起,再次獲得IO [Int]類型的東西!

現在繼續嘗試完全一樣的東西:以我們剛剛創建的IO [Int]對象一起膠水(使用>>=return)與reverse :: [Int] -> [Int]

我覺得我寫的太多了,但是讓我知道是否有什麼不清楚的地方,或者如果你需要其他幫助。

什我到目前爲止所描述可以是這個樣子:

getContent :: IO [Int] 
getContent = return [5,2,1,7] 

main :: IO() 
main = do 
    listOfInts <- getContent 
    return $ sort listOfInts 
    return()     -- This is only to sattisfy the signature of main