[英]Haskell Input to create a String List
我想允許用戶從 Haskell 中的一系列輸入構建一個列表。
getLine 函數將被遞歸調用,直到輸入停止條件(“Y”),此時返回列表。
我知道該功能需要采用與以下類似的格式。 我在分配正確的類型簽名時遇到問題 - 我想我需要在某處包含 IO 類型。
getList :: [String] -> [String]
getList list = do line <- getLine
if line == "Y"
then return list
else getList (line : list)
所以有很多事情你需要理解。 其中之一是IO x
類型。 這種類型的值是一個計算機程序,當稍后運行時,它會做一些事情並產生一個類型為x
的值。 所以getLine
本身不做任何事情; 它僅僅是某種特定類型節目。 與let p = putStrLn "hello!"
. 我可以多次將p
序列化到我的程序中,它會打印出hello!
多次,因為IO ()
是一個程序,作為一個 Haskell 恰好能夠談論和操作的值。 如果這是 TypeScript,我會說type IO<x> = { run: () => Promise<x> }
並強調該 type 表示副作用動作尚未運行。
那么當值是一個程序時我們如何操作這些值,例如一個獲取當前系統時間的程序?
將這些程序鏈接在一起的最基本方法是采用一個產生x
(一個IO x
)的程序,然后一個 Haskell 函數,該函數采用一個x
並構造一個產生y
(一個x -> IO y
並將它們組合起來的程序)一起形成一個產生y
(一個IO y
)的結果程序。這個函數被稱為>>=
並發音為“綁定”。事實上,這種方式是通用的,如果我們添加一個程序,該程序采用x
類型的任何 Haskell 值並產生一個什么都不做並產生該值的程序( return :: x -> IO x
)。例如,這允許您使用 Prelude 函數fmap f = (>>= return . f)
,它接受a -> b
和將其應用於IO a
以生成IO b
。
所以像getLine >>= \\line -> putStrLn (upcase line ++ "!")
這樣的東西很常見,我們發明了do
-notation,把它寫成
do
line <- getLine
putStrLn (upcase line ++ "!")
請注意,這是相同的基本交易; 最后一行需要是一些y
的IO y
y
。
在 Haskell 中你需要知道的最后一件事是實際運行這些東西的約定。 也就是說,在您的 Haskell 源代碼中,您應該創建一個名為Main.main
的IO ()
(一個值無關緊要的程序),並且 Haskell 編譯器應該采用您描述的這個程序,並給出它作為可執行文件提供給您,您可以隨時運行。 作為一個非常特殊的情況,GHCi 解釋器會注意到您是否在頂層生成了一個IO x
表達式,並會立即為您運行它,但這與語言其他部分的工作方式大不相同。 Haskell 說,在大多數情況下,請描述該程序,然后我將其提供給您。
既然您知道 Haskell 沒有魔法,而 Haskell IO x
類型只是作為值的計算機程序的靜態表示,而不是在您“減少”它時會產生副作用的東西(就像在其他語言中一樣) ),我們可以求助於您的getList
。 顯然getList :: IO [String]
根據你所說的最有意義:一個允許用戶從一系列輸入構建列表的程序。
現在要構建內部結構,您的猜測是正確的:我們必須從getLine
開始,然后完成列表或繼續接受輸入,將行添加到列表中:
getList = do
line <- getLine
if line == 'exit' then return []
else fmap (line:) getList
您還確定了另一種方法,這取決於獲取字符串列表並生成新列表:
getList :: IO [String]
getList = fmap reverse (go []) where
go xs = do
x <- getLine
if x == "exit" then return xs
else go (x : xs)
可能還有其他幾種方法可以做到。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.