简体   繁体   English

Haskell 输入以创建字符串列表

[英]Haskell Input to create a String List

I would like to allow a user to build a list from a series of inputs in Haskell.我想允许用户从 Haskell 中的一系列输入构建一个列表。

The getLine function would be called recursively until the stopping case ("Y") is input, at which point the list is returned. getLine 函数将被递归调用,直到输入停止条件(“Y”),此时返回列表。

I know the function needs to be in a similar format to below.我知道该功能需要采用与以下类似的格式。 I am having trouble assigning the correct type signatures - I think I need to include the IO type somewhere.我在分配正确的类型签名时遇到问题 - 我想我需要在某处包含 IO 类型。

getList :: [String] -> [String]
getList list =  do line <- getLine
                   if line ==  "Y"
                      then return list
                      else getList (line : list)

So there's a bunch of things that you need to understand.所以有很多事情你需要理解。 One of them is the IO x type.其中之一是IO x类型。 A value of this type is a computer program that, when later run, will do something and produce a value of type x .这种类型的值是一个计算机程序,当稍后运行时,它会做一些事情并产生一个类型为x的值。 So getLine doesn't do anything by itself;所以getLine本身不做任何事情; it just is a certain sort of program.它仅仅某种特定类型节目。 Same with let p = putStrLn "hello!"let p = putStrLn "hello!" . . I can sequence p into my program multiple times and it will print hello!我可以多次将p序列化到我的程序中,它会打印出hello! multiple times, because the IO () is a program, as a value which Haskell happens to be able to talk about and manipulate.多次,因为IO ()是一个程序,作为一个 Haskell 恰好能够谈论和操作的值。 If this were TypeScript I would say type IO<x> = { run: () => Promise<x> } and emphatically that type says that the side-effecting action has not been run yet .如果这是 TypeScript,我会说type IO<x> = { run: () => Promise<x> }并强调该 type 表示副作用动作尚未运行

So how do we manipulate these values when the value is a program, for example one that fetches the current system time?那么当值是一个程序时我们如何操作这些值,例如一个获取当前系统时间的程序?

The most fundamental way to chain such programs together is to take a program that produces an x (an IO x ) and then a Haskell function which takes an x and constructs a program which produces a y (an x -> IO y and combines them together into a resulting program producing a y (an IO y .) This function is called >>= and pronounced "bind". In fact this way is universal, if we add a program which takes any Haskell value of type x and produces a program which does nothing and produces that value ( return :: x -> IO x ). This allows you to use, for example, the Prelude function fmap f = (>>= return . f) which takes an a -> b and applies it to an IO a to produce an IO b .将这些程序链接在一起的最基本方法是采用一个产生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

So It is so common to say things like getLine >>= \\line -> putStrLn (upcase line ++ "!") that we invented do -notation, writing this as所以像getLine >>= \\line -> putStrLn (upcase line ++ "!")这样的东西很常见,我们发明了do -notation,把它写成

do 
    line <- getLine
    putStrLn (upcase line ++ "!")

Notice that it's the same basic deal;请注意,这是相同的基本交易; the last line needs to be an IO y for some y .最后一行需要是一些yIO y y

The last thing you need to know in Haskell is the convention which actually gets these things run.在 Haskell 中你需要知道的最后一件事是实际运行这些东西的约定。 That is that, in your Haskell source code, you are supposed to create an IO () (a program whose value doesn't matter) called Main.main , and the Haskell compiler is supposed to take this program which you described, and give it to you as an executable which you can run whenever you want.也就是说,在您的 Haskell 源代码中,您应该创建一个名为Main.mainIO () (一个值无关紧要的程序),并且 Haskell 编译器应该采用您描述的这个程序,并给出它作为可执行文件提供给您,您可以随时运行。 As a very special case, the GHCi interpreter will notice if you produce an IO x expression at the top level and will immediately run it for you, but that is very different from how the rest of the language works.作为一个非常特殊的情况,GHCi 解释器会注意到您是否在顶层生成了一个IO x表达式,并会立即为您运行它,但这与语言其他部分的工作方式大不相同。 For the most part, Haskell says, describe the program and I will give it to you. Haskell 说,在大多数情况下,请描述该程序,然后我将其提供给您。

Now that you know that Haskell has no magic and the Haskell IO x type just is a static representation of a computer program as a value, rather than something which does side-effecting stuff when you "reduce" it (like it is in other languages), we can turn to your getList .既然您知道 Haskell 没有魔法,而 Haskell IO x类型只是作为值的计算机程序的静态表示,而不是在您“减少”它时会产生副作用的东西(就像在其他语言中一样) ),我们可以求助于您的getList Clearly getList :: IO [String] makes the most sense based on what you said: a program which allows a user to build a list from a series of inputs.显然getList :: IO [String]根据你所说的最有意义:一个允许用户从一系列输入构建列表的程序。

Now to build the internals, you've got the right guess: we've got to start with a getLine and either finish off the list or continue accepting inputs, prepending the line to the list:现在要构建内部结构,您的猜测是正确的:我们必须从getLine开始,然后完成列表或继续接受输入,将行添加到列表中:

getList = do
    line <- getLine
    if line == 'exit' then return []
                      else fmap (line:) getList

You've also identified another way to do it, which depends on taking a list of strings and producing a new list:您还确定了另一种方法,这取决于获取字符串列表并生成新列表:

getList :: IO [String]
getList = fmap reverse (go []) where 
    go xs = do 
        x <- getLine
        if x == "exit" then return xs
                       else go (x : xs)

There are probably several other ways to do it.可能还有其他几种方法可以做到。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM