Haskell 输入以创建字符串列表
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> }
并强调该类型表示副作用操作 还没有 运行 .
那么当值是一个程序时,我们如何操作这些值,例如获取当前系统时间的程序?
将这些程序链接在一起的最基本方法是采用一个产生 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
表示法,写成
do
line <- getLine
putStrLn (upcase line ++ "!")
请注意,这是相同的基本交易;对于某些 y
.
,最后一行需要是 IO y
在 Haskell 中你需要知道的最后一件事是实际上得到这些东西的约定 运行。也就是说,在您的 Haskell 源代码中,您应该创建一个名为 Main.main
的 IO ()
(一个值无关紧要的程序)和 Haskell 编译器应该采用您描述的这个程序,并将其作为可执行文件提供给您,您可以随时 运行 。作为一种非常特殊的情况,如果您在顶层生成 IO x
表达式,GHCi 解释器会注意到,并且会立即 运行 为您提供它,但这是与其他语言的工作方式非常不同。大多数情况下,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)
可能还有其他几种方法。
我想允许用户根据 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> }
并强调该类型表示副作用操作 还没有 运行 .
那么当值是一个程序时,我们如何操作这些值,例如获取当前系统时间的程序?
将这些程序链接在一起的最基本方法是采用一个产生 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
表示法,写成
do
line <- getLine
putStrLn (upcase line ++ "!")
请注意,这是相同的基本交易;对于某些 y
.
IO y
在 Haskell 中你需要知道的最后一件事是实际上得到这些东西的约定 运行。也就是说,在您的 Haskell 源代码中,您应该创建一个名为 Main.main
的 IO ()
(一个值无关紧要的程序)和 Haskell 编译器应该采用您描述的这个程序,并将其作为可执行文件提供给您,您可以随时 运行 。作为一种非常特殊的情况,如果您在顶层生成 IO x
表达式,GHCi 解释器会注意到,并且会立即 运行 为您提供它,但这是与其他语言的工作方式非常不同。大多数情况下,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)
可能还有其他几种方法。