在Haskell中,用户如何将项目添加到列表中

在Haskell中,用户如何将项目添加到列表中,haskell,Haskell,我了解如何使用递归数据结构来管理内容列表: data Thingy a = NoThingy | Thingy a a (Thingy a) deriving (Show, Read) firstThingsFirst :: a -> a -> (Thingy a) firstThingsFirst a b = Thingy a b NoThingy andAnotherThing :: a -> a -> Thingy a -> Thingy a andAno

我了解如何使用递归数据结构来管理内容列表:

data Thingy a = NoThingy | Thingy a a (Thingy a) deriving (Show, Read)

firstThingsFirst :: a -> a -> (Thingy a)
firstThingsFirst a b = Thingy a b NoThingy

andAnotherThing :: a -> a -> Thingy a -> Thingy a
andAnotherThing a b NoThingy = Thingy a b NoThingy
andAnotherThing a b things = Thingy a b things
在ghci中,我可以做如下事情:

let x=andAnotherThing "thing1" "thing2" NoThingy
let y=andAnotherThing "thing3" "thing4" x
import System.IO
allThings=NoThingy
main = do
  putStrLn "First Thing"
  first<-getLine
  putStrLn "Second Thing"
  second<-getLine
  let allThings=Thingy first second allThings
  print allThings
  main
let allThings = Thingy (read first :: Int) (read second :: String) allThings
然而,我不知道如何使这项工作为一个编译程序,需要用户输入。换句话说,我想让用户填充结构。比如:

let x=andAnotherThing "thing1" "thing2" NoThingy
let y=andAnotherThing "thing3" "thing4" x
import System.IO
allThings=NoThingy
main = do
  putStrLn "First Thing"
  first<-getLine
  putStrLn "Second Thing"
  second<-getLine
  let allThings=Thingy first second allThings
  print allThings
  main
let allThings = Thingy (read first :: Int) (read second :: String) allThings
import System.IO
万物=虚无
main=do
putStrLn“第一件事”

首先听起来像是在寻求一种方法,将用户输入的
String
类型转换为
Thingy
类型的值。由于您的
Thingy
类型已经有了
Read
的实例,您可以使用
Read
功能:

read :: Read a => String -> a
这使您的
main
功能更像:

main = do
  putStrLn "First Thing" 
  first <- getLine
  putStrLn "Second Thing"
  second <- getLine
  let allThings = Thingy (read first) (read second) allThings
  print allThings
  main
另一个问题是,类型推断无法猜测要使用哪种类型
a
,因此您还必须给出提示,例如:

let x=andAnotherThing "thing1" "thing2" NoThingy
let y=andAnotherThing "thing3" "thing4" x
import System.IO
allThings=NoThingy
main = do
  putStrLn "First Thing"
  first<-getLine
  putStrLn "Second Thing"
  second<-getLine
  let allThings=Thingy first second allThings
  print allThings
  main
let allThings = Thingy (read first :: Int) (read second :: String) allThings

可能值得注意的是,在这个定义中,
allThings
是一个无限的结构,并且将永远无法打印:最后一次调用
main
将永远无法到达。

您创建的是一个可以打印的无限自我引用的“allThings”

您将绑定到名称allThings两次。第一次在主屏幕前,第二次在打印前

第二个绑定指的是位于右侧末尾的所有内容。此引用不是对第一个绑定的引用。此引用指向第二个绑定本身

如果更改第二个装订和打印的名称:

main = do
  putStrLn "First Thing" 
  first <- getLine
  putStrLn "Second Thing"
  second <- getLine
  let allThings2 = Thingy (read first) (read second) allThings
  print allThings2
  main
main=do
putStrLn“第一件事”

首先,问题是您试图使用一个变量来保存可变状态(就像命令式语言中的变量一样),但在Haskell中无法这样做所有状态必须作为参数显式传递给函数。正如其他答案所指出的,main中的
allThings
实际上是全局范围内的
allThings
的一个单独变量(它只是隐藏相同的名称)

下面的示例演示如何构建一个程序,该程序在构建数字列表的同时始终循环。我认为这正是你想要做的事情,让它适应“东西”应该不难

在我们的例子中,我们必须为循环保留的状态是下一个要输入的数字(1、2等)的索引和我们已经读取的数字列表。因此,我们的循环函数将接收此状态作为参数,并返回IO操作

module Main where

-- Explicit signature so readLn and show don't complain...
loop_step :: (Int, [Int]) -> IO ()
loop_step (i,xs) = do
    putStrLn ("Enter the " ++ show i ++ "th number:")
    n <- readLn
    let newList = n : xs
    print newList
    loop_step (i+1, newList)

main :: IO ()
main = do
    loop_step (1, [])
modulemain其中
--显式签名,以便读取和显示,不要抱怨。。。
循环步骤::(Int,[Int])->IO()
循环_步骤(i,xs)=do
putStrLn(“输入“++显示i+”第个数字:”)

nHaskell中的值是不可变的,因此如果您“向列表中添加一项”,您将得到一个新列表。因此,在上面的代码中

let allThings = Thingy first second allThings
没有达到你的期望。顶级的allThings具有值
NoThingy
,并且该值不能更改。let绑定中的名称
allThings
不引用顶级实体,它引入了一个新的绑定来隐藏顶级名称,并且该新名称也在绑定的右侧引用。 所以这条线和下面的是等价的

let theThings = Thingy first second theThings
print theThings
let绑定创建一个循环结构,将自身称为其组件之一。这当然意味着打印永远不会完成

您(可能)想要执行的操作需要将要更新的结构作为参数传递

loop things = do
        putStrLn "First Thing"
        ...
        let newThings = Thingy first second things
        print newThings
        loop newThings

当然,正如Nicolas所说,您可能希望将输入字符串转换为适当类型的值。

的第一个子句是多余的,因为第二个子句做同样的事情。就这件事而言,
和任何东西都是多余的,因为它和
东西一样。谢谢所有回答的人。正如你们大多数人所说明的,创建一个循环并从main调用它正是我想要的。顺便说一下,您可以使用内置的
[(a,a)]
而不是
东西a