我理解如何使用递归数据结构来管理一系列内容:
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
答案 0 :(得分:11)
Haskell中的值是不可变的,因此如果您“将项目添加到列表中”,您将获得一个新列表。所以在上面的代码中,
let allThings = Thingy first second allThings
不符合您的期望。顶级allThings的值为NoThingy
且无法更改。 let-binding中的名称allThings
不引用顶级实体,它引入了一个新的绑定,遮蔽了顶级名称,并且右侧也引用了新名称的约束力。
所以该行和以下内容相当于
let theThings = Thingy first second theThings
print theThings
let-binding创建一个循环结构,将自身称为其组件之一。这当然意味着打印它永远不会完成。
您(可能)想要做的事情要求将要更新的结构作为参数传递
loop things = do
putStrLn "First Thing"
...
let newThings = Thingy first second things
print newThings
loop newThings
当然,就像尼古拉斯所说的那样,您可能希望将输入字符串转换为适当类型的值。
答案 1 :(得分:2)
你创造的是一种无限自我指导的“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
然后你会在每个主循环上打印一个Thingy。由于你想积累答案,你可以定义一个尾递归“查询”,如下所示:
query old = do
putStrLn "First Thing"
first<-getLine
putStrLn "Second Thing"
second<-getLine
let new=Thingy first second old
print new
query new
main = query NoThingy
以上可能会做你想要的。
答案 2 :(得分:1)
听起来您要求的方法是将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
当然,如果您输入的字符串与Thingy不对应,那么您将收到错误消息,因此您可能希望使用safe package中的readMay
代替:
readMay :: Read a => String -> Maybe a
另一个问题是,类型推断无法猜出a
的类型是什么,所以你也必须给它一个提示,可能有类似的东西:
let allThings = Thingy (read first :: Int) (read second :: String) allThings
值得注意的是,在这个定义中,allThings
是一个无限结构,将永远打印:永远不会达到对main
的最后一次调用。
答案 3 :(得分:1)
问题是你正在尝试使用变量来保持可变状态(就像在命令式语言中那样)但你不能在Haskell中这样做。 必须将所有州明确作为参数传递给您的函数。正如其他答案中所指出的,main allThings
实际上是一个与全局范围内的allThings
分开的变量(它只是隐藏了相同的名称)
以下示例显示如何构建一个在构建数字列表时永远循环的程序。我认为这是你想要做的事情,并不应该很难适应“Thingies”
在我们的例子中,我们必须为循环保留的状态是要输入的下一个数字的索引(第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, [])