美文网首页
Haskell学习笔记(一)

Haskell学习笔记(一)

作者: maxkibble | 来源:发表于2017-06-17 11:14 被阅读0次

这一系列的笔记主要参考中文版的 Real World Haskell,这篇博文作为本系列的第一篇,先介绍一下Haskell的语法和一些基本知识。


进入/退出交互模式ghci

安装好Haskell,命令行敲ghci进入交互模式

Prelude> :set prompt “ghci>”
ghci>:q                       -- 退出

基本算术

ghci> 1 + 1
2

ghci> (+) 1 1
2

ghci> 2 + (-1)     -- 负数是通过唯一的一元操作符'-'得到,-1的括号不能省略
1

ghci> True && False
False

ghci> True || False
True

ghci> :info (+)      -- 操作符的信息可以通过':info'方式查看
class Num a where
  (+) :: a -> a -> a
  ...
    -- Defined in ‘GHC.Num’
infixl 6 +           -- infixl代表加法是左结合的,优先级为6

ghci> :info (^)
(^) :: (Num a, Integral b) => a -> b -> a   -- Defined in ‘GHC.Real’
infixr 8 ^        -- 乘方是右结合的,指数b的类型是Integer,优先级为8

ghci> not True    -- not是一个内置函数,不可以通过:info查看其信息
False

ghci> 1 == 1
True

ghci> 1 /= 1
False

ghci> let e = exp 1     -- 添加绑定
ghci> e ^ 1
2.718281828459045
ghci> e ** pi      -- 指数不是整数时,需要更换操作符
23.140692632779263

列表和元组

列表表示一系列相同类型的元素集合,元组则可以包括不同类型的元素,void在Haskell中被实现为一个空的元组

ghci> [1,2]    -- 列表
[1,2]

ghci> [1..5]
[1,2,3,4,5]

ghci> [1,3..9]
[1,3,5,7,9]

ghci> [1] ++ [2,3]  -- 列表的连接
[1,2,3]

ghci> 1 : [2,3]  -- 元素和列表的连接,类似cons
[1,2,3]

ghci> head [1,2,3]   -- 返回列表的第一个元素
1

ghci> tail [1,2,3]   -- 返回除去第一个元素后的剩余列表
[2,3]

ghci> last [1,2,3]   -- 返回列表的最后一个元素
[3]

ghci> init [1,2,3]   -- 返回除去最后一个元素的剩余列表
[1,2]

ghci> reverse [1,2,3]
[3,2,1]

ghci> take 2 [1,2,3]  -- 返回列表的前n个元素组成的新列表
[1,2]

ghci> drop 1 [1,2,3]  -- 返回丢弃前n个元素后得到的新列表
[2,3]

ghci> null []
True

ghci> (1, "Hello")    -- 元组
(1, "Hello")

ghci> fst (1,"Hello")  -- 取元组的第一个元素
1

ghci> snd (1,"Hello")  -- 取元组的第二个元素
"Hello" 

字符串

Haskell的字符串和C的非常相似,用单引号表示单个字符,双引号代表字符串,字符串实际就是单个字符的列表,但不需要像C中用'\0'结尾

ghci> 'a'
'a'

ghci> "Hello World"
"Hello World"

ghci> ['H','e','l','l','o']
"Hello"

ghci> "" == []
True

ghci> 'a' : "bc"
"abc"

ghci> "a" ++ "bc"
"abc"

查看表达式类型

在交互式界面下,我们有两种方法查看表达式的类型:

ghci> :set +t
ghci > 'a'
'a'
it :: Char    -- it是交互模式下存储运算结果的变量
ghci> :unset +t
ghci> 'a'
'a'

ghci> :type 'a'
'a' :: Char
ghci> :type 1 + 1
1 + 1 :: Num a => a    -- :type执行静态的类型推导,并不会显示运算结果2的类型

分数

ghci> :m +Data.Ratio      -- ':m'代表载入模块
ghci> :set +t
ghci> 1 % 2               -- 分数1/2
1 % 2
it :: Integral a => Ratio a

整数范围

Haskell中的整数是不限大小的,你可以写出下面的式子:

ghci> 313 ^ 15
27112218957718876716220410905036741257

自定义函数

在ghci中定义函数的语法和标准Haskell有所不同,我们在后者环境下定一个函数add,并加载到ghci环境中:

-- this is in myDrop.hs
myDrop n xs = if n <= 0 || null xs
              then xs
              else myDrop (n - 1) (tail xs)
-- back to ghci mode
ghci> :load myDrop.hs
ghci> myDrop 2 "cat"
"t"

副作用

避免函数的副作用有很多好处,在Haskell中,定义的函数默认都是无副作用的(纯函数),有副作用的函数在Haskell中类型会带有IO标签:

ghci> :type readFile
readFile :: FilePath -> IO String

纯度减轻了理解一个函数所需的工作量。一个纯函数的行为并不取决于全局变量、数据库的内容或者网络连接状态。纯代码(pure code)从一开始就是模块化的:每个函数都是自包容的,并且都带有定义良好的接口。
将纯函数作为默认的另一个不太明显的好处是,它使得与不纯代码之间的交互变得简单。一种常见的 Haskell 风格就是,将带有副作用的代码和不带副作用的代码分开处理。在这种情况下,不纯函数需要尽可能地简单,而复杂的任务则交给纯函数去做。
软件的大部分风险,都来自于与外部世界进行交互:它需要程序去应付错误的、不完整的数据,并且处理恶意的攻击,诸如此类。Haskell 的类型系统明确地告诉我们,哪一部分的代码带有副作用,让我们可以对这部分代码添加适当的保护措施。
通过这种将不纯函数隔离、并尽可能简单化的编程风格,程序的漏洞将变得非常少。

变量

在Haskell中,一个变量和一个值建立绑定之后,这个变量的值就不可以再被修改了,举个例子,下面的代码是无法通过编译的:

-- file: ch02/Assign.hs
x = 10
x = 11

命令式语言中的变量是和一个内存地址建立了绑定,但地址里写的内容是可以反复修改的,所以在命令式语言中不同时刻读取同一变量的值可能会得到不同的结果。在Haskell中,变量是和值建立的绑定,一个变量被绑定后,我们总能把变量替换成绑定的值。

惰性求值

Haskell中用于追踪未求值表达式的记录被称为块。编译器通过创建块来延迟表达式的求值,直到这个表达式的值真正被需要为止。如果某个表达式的值不被需要,那么从始至终,这个表达式都不会被求值。在Haskell中可以定义无限长的数组,并可以对其进行一些操作:

-- mycons.hs
mycons n = n : mycons (n + 1)
l = mycons 0       -- l是自然数集合

-- ghci
ghci> :load mycons.hs
ghci> take 3 l
[0,1,2]
ghci> (!!) l 0    -- !!是根据下标取列表元素的操作,从0开始
0
ghci> takeWhile(\x->x<3) l
[0,1,2]

Haskell还自带了一个repeat::a->[a]函数,他接受一个参数x,返回一个无限个x组成的列表。

类型系统

Haskell的类型系统基于简洁而强大的 System F,这里我们不深究其实现,只列出Haskell类型系统的三个特性:

  • 强类型:强类型语言不会做隐式的类型转换。举个例子,如果将一个整数值作为参数传给了一个接受浮点数的函数,C 编译器会自动且静默(silently)地将参数从整数类型转换为浮点类型,而 Haskell 编译器则会引发一个编译错误。
  • 静态类型:静态类型的语言会在编译阶段求出所有变量和表达式的类型,并拒绝执行任何类型错误的程序。同时,Haskell还提供了 typeclass 机制让程序员实现一些动态类型语言的功能。
  • 自动推导:如果没有自动推导,那么一门静态语言就要求程序员事无巨细地显示声明所有变量的类型以及类型之间的关系,就像Java采用的norminal type system。和很多函数式语言一样,Haskell可以几乎自动推导出所有表达式的类型(有时需要程序员提供一些必要的提示)。这使得程序员在编码时几乎不需要写任何的类型信息(写出来当然也是可以的),但同时编译时期也有强大的类型检查保证Haskell代码的类型安全。

多态

Haskell中列表中的值可以是任意类型,所以我们可以称列表为类型多态(polymorphic)的。任何列表相关的操作函数应当也是多态的,看一个例子:

ghci> :type last
ghci> last :: [a] -> a

这里last的类型中包含了一个类型变量a,在不同情况下它会具化成不同的类型,当last函数应用于一个字符串的时候,其类型就是[Char]->Char,而应用于一个整数数组时,last的类型就是[Integer]->Integer

相关文章

  • Haskell学习笔记(一)

    这一系列的笔记主要参考中文版的 Real World Haskell,这篇博文作为本系列的第一篇,先介绍一下Has...

  • [2016-09] 九月小结

    每月一次总结,回顾过去,展望未来。 学习 《Real World Haskell》前5章:读书笔记链接:《Real...

  • Haskell学习-函数式编程初探

    原文地址:Haskell学习-函数式编程初探  为什么要学习函数式编程?为什么要学习Haskell?  .net到...

  • haskell 学习笔记 (持续更新)

    第一章 类型系统和函数 类型 数据类型 Bool Char Int Word Integer Float Doub...

  • Haskell学习笔记--类型推导

    Haskell类型推导 a = a + 1 在命令式编程的时代,区分一个人是否能学会编程的关键是看他能否理解a=a...

  • Haskell语言学习笔记

    本文是对Haskell语言学习的一个简单总结,包括如下章节的内容: 概述 编写第一个程序 函数定义 IO操作 Ha...

  • Haskell学习笔记(一) 环境的搭建

    Haskell简介 ​ Haskell是一种标准化的、通用纯函数式编程语言,有非限定性语义和强静态类型。它的命...

  • haskell学习

    工具 haskell platform,直接百度安装. 打开控制台输入ghci即进入交互模式。 假如定义了myfu...

  • haskell学习

    基本变量类型Int:代表有范围的整数Integer: 代表没有范围的整数Float,Double,Bool,Cha...

  • 学习 Haskell

    上一次学习 Haskell 可以追溯到大二的时候,当时看了一本名为《七周七语言》的书,其中一门语言就是 Has...

网友评论

      本文标题:Haskell学习笔记(一)

      本文链接:https://www.haomeiwen.com/subject/sdurqxtx.html