输入和输出
未完,待续~~~~~~~~~~~~(博客里的Markdown有点不如人意,请见谅)
前面已提到过Haskell是纯函数语言。在命令式语言中你可以指定一系列步骤让计算机去执行,然后得到结果,而函数语言则更偏向于定义它们。
在Haskell里,函数不能更改状态,例如改变变量的值(如果一个函数改变了状态,我们说它存在副作用)。
在Haskell里,函数只能做的事就是根据我们传给它的参数返回结果。如果给一个函数传递相同的参数,那么它应该始终返回相同的结果。
如果你习惯于命令式语言,刚开始这可能觉得被限制了,但很快你就能体会到其实这很酷。
在命令式语言中,即使是一个简单的函数,你也不能保证在处理数据时,它不会放火烧你的房子、绑架你的狗、用土豆刮伤你的汽车。(译注:这里是幽默的说法,意思是存在副作用)
举例说明,就是当我们创建二叉搜索树时,我们插入新元素时不能修改当前的树。
表面上,我们是插入新元素到当前二叉树中,但实际上却是我们创建了一棵新树,而不是修改当前树。
函数不能改变状态,看起来很酷,它能够让我们的程序更可靠。只是还有一个问题 ^-^,如果函数不能改变任何东西,那它怎么告诉我们计算结果?
为了告诉我们计算结果,它只能改变我们的输出设备(通常就是屏幕了^-^),设备发射光子到我们的眼睛,改变我们头脑的状态。^_^
不要绝望,还是有办法解决滴。实际上Haskell有一个聪明的脑袋,可以在函数中区分出存在副作用和纯净的部分。将存在副作用的代码与我们的程序分离出来,留下纯净的代码
与我们交互。存在副作用的场景可能是我们与键盘或屏幕的交互。
因为区分出了两部分,所以我们的程序还是很可靠的。与外界沟通时,我们的代码仍然具有惰性、鲁棒性、和模块化。
Hello, world!
在这之前,我们经常用GHCI来加载自己的函数并且玩得不亦乐乎。我们也探讨了一些标准库函数。
现在,经过了8个章节的学习后,我们将真正开始写第一个Haskell程序。耶!和平常一样,我们将以"hello, world"起头。
注意啦!在此章节里,我将假设你用的是*inux环境来学习Haskell。如果你使用的是Windows,我建议你下载Cygwin,它可以让你在Windows下 有类Linux的感觉。
现在,用你喜欢的文本编辑器敲下面的代码。
main = putStrLn "hello, world"
我们定义了一个main
函数,它调用了putStrLn
函数,参数为"hello, world"
。
看,就这么简单,一分钟都不到。保存文件为helloword.hs
。
现在,让我们做一些从未做过的事——真正的编译我们的代码。太让我兴奋了!
打开终端并导航到helloword.hs
所在目录,敲下面命令:
$ ghc --make helloworld
[1 of 1] Compiling Main ( helloworld.hs, helloworld.o )
Linking helloworld ...
Okay!不出意外的话,你会看到结果。然后你就可用./helloword
跑你的程序了。
$ ./helloworld
hello, world
到此结束,我们的第一个程序经过编译并在终端打印出了结果。这也太无聊了吧!
让我们回顾一下刚才到底写了什么。首先,看看函数putStrLn
是什么类型。
ghci> :t putStrLn
putStrLn :: String -> IO ()
ghci> :t putStrLn "hello, world"
putStrLn "hello, world" :: IO ()
我们可像这么读putStrLn
的类型:putStrLn
取一字符串,返回一I/O 操作
结果类型是()
(例如,空元组,也可以称为单元)。I/O 操作可以这么理解,将执行一个含有副作用的操作(通常是读取输入或者打印输出到屏幕)并以某种形式包含在返回值中。
打印字符串到终端并不是真正意义上的返回值,所以用虚拟的值()
代替。
空元组的值为()
,并且它的类型也是()
。
那么,I/O 操作到底什么时候被执行?答案是,当执行到main
时。当我们执行到main
函数时,就会执行I/O 操作。
在你的整个程序里,只能有一个I/O 操作看起来是一种限制。这就是为什么我们能用 do 语法将几个I/O 操作组合成一个的原因。看看下面的例子:
main = do
putStrLn "Hello, what's your name?"
name <- getLine
putStrLn ("Hey " ++ name ++ ", you rock!")
啊哈,新语法,看起来很有趣!并且它看起来有点像命令式编程。编译并运行它,它的结果和你所想的可能完全一样。
注意,我们写 do 之后,列出了一系列的步骤——就像在命令式编程里一样。每一个步骤都是一 I/O 操作。
用 do 语法将它们组合成一个 I/O 操作。此操作的类型为 IO ()
,因为最后的I/O 操作就是这个类型。
正因如此,main
函数经常有类型签名 main :: IO something
,something
是具体的类型。
按照惯例,我们通常不直接声明 main
的类型。
一切版权归原作者所有,但此译文版权公用。引用时如能保留译者leekelby, 将不甚感激!
Haskell本人初学乍到,如对翻译有任何批评或建议,欢迎留言或给我Email, leekelby AT gmail DOT com