本文的主要内容参考自《Haskell趣学指南》

1. What is Haskell?

以下内容引用自[Haskell](https://www.haskell.org/)官网:
>Haskell是一个先进的,纯粹的函数式编程语言。一个典型的声明式地,静态类型的代码如下:
```haskell
primes = filterPrime [2..]
where filterPrime (p:xs) =
p : filterPrime [x | x - **静态类型**(Statically Typed)。Haskell的每一个表达式都有一个在编译时决定的类型。所有的由函数引用组合起来的类型必须相匹配(match up),否则无法正常编译。类型不仅仅是形式上的保证,更是用于表达程序结构的语言。
> - **纯粹函数式**(Purely functional)。Haskell的每一个函数都是数学意义上的(pure)。即使是有副作用的IO操作也不过是在描述在做什么,同样由纯粹的代码产生。没有声明或者指令,仅仅只有表达式,该表达式不能对变量进行修改(局部变量或者全局变量),或者是获取像时间、随机数这样的状态。
> - **类型推断**(Type Inference)。你并不需要在Haskell中显式地写出每一种类型,类型将会进行双向推断。当然,你也可以选择自己写出类型,或者让编译为你进行推断。
> - **并发**(Concurrent)。Haskell可以很容易进行并发编程,这得力于它可以显式地处理effects。它的王牌编译器GHC带有一个高性能的并行的垃圾回收器,还有一个轻量级的并发库,其中包含了很多的有用的并发函数原型以及抽象接口。
> - **惰性计算**(Lazy)。函数并不会直接计算它们的值。这意味着程序可以在一起组合地非常好,可以通过只写通常的函数就表达控制结构(if/else)。Haskell代码的纯粹性使得它可以轻松将函数链式组合,操作起来非常方便。
> - **包**(Packages)。你可以在public packages server 上找到非常多的活跃的开源Haskell packages。

2. How to use Haskell?

你可以下载Haskell的GHC编译器,该编译器可以解释也可以编译Haskell程序。GHC还有一个很有用的交互模式,在终端输入ghci即可进入交互模式。然后运行命令`:l demo.hs`就可以加载demo.hs中的函数(Haskell程序文件以hs结尾)。如果你对demo.hs做了修改,可以重新运行命令`:l demo.hs`以重新加载其中的函数。这是我们以后实验学习Haskell的基本流程。

3. Basic Operations

3.1 基本类型

在终端输入ghci进行入交互模式。如下是一些基本的运算,几乎不用解释就可以看懂。
```haskell
Prelude> 2+15
17
Prelude> 45*90
4050
Prelude> 568-23
545
Prelude> 3/2
1.5
Prelude> (90-23)*8
536
Prelude> 50*(100-9028)
-446400
Prelude> True && False
False
Prelude> True && True
True
Prelude> False || True
True
Prelude> not False
True
Prelude> not (True && True)
False
Prelude> 5 == 4
False
Prelude> 5 == 5
True
Prelude> "Hello" == "Hello"
True
Prelude> 5 /= 4
True
Prelude> 5 /= 5
False
```

3.2 基础函数

#### 3.2.1 Haskell 常用内置函数
- **succ**。succ 函数返回一个数的后继。比如:
```haskell
Prelude> succ 10
11
```
注意Haskell中函数的调用参数是使用空格的,这一点和传统的编译型语言有很大的不同。
- **pred**。pred函数和succ函数相反,它返回一个数字的前驱。比如:
```haskell
Prelude> pred 10
9
```
- **min**。min函数接收两个数字参数,返回这两个数字的最小值。比如:
```haskell
Prelude> min 10 3.4
3.4
```
如果要比较多个数字的最小值,直接传递多个数字作为参数会报错,比如:
```haskell
Prelude> min 1 19 23
:21:1:
Non type-variable argument in the constraint: Num (a -> t)
(Use FlexibleContexts to permit this)
When checking that ‘it’ has the inferred type
it :: forall a t. (Num a, Num (a -> t), Ord (a -> t)) => t
```
我们可以通过多次调用min函数(注意需要加括号)来解决:
```haskell
Prelude> min 1 (min 9 23)
1
```
- **max**。max函数返回两个数字的最大值。比如:
```haskell
Prelude> max 9.9 9.0
9.9
```
- Haskell中**函数**拥有最高的优先级。下面两句是等效的:
```haskell
Prelude> succ 10 + max 10 20 * 3
71
Prelude> (succ 10) +(max 10 20)*3
71
```
注意`succ 9*10`的值是100而不是91,这是因为计算的顺序是先计算`succ 9`得到10,然后再计算`10*10=100`,如果我们要计算9*10的后继,需要写成`succ (9*10)`的形式。
- **div**。div函数计算一个整数除以另外一个整数的结果,比如:
```haskell
Prelude> div 10 3
3
Prelude> div 10 5
2
```
需要注意的是,div函数接收的两个参数必须是整数,第一个是被除数,第二个是除数,如果除不尽则返回商,舍弃余数。传入数字不能是浮点数,比如下面的形式会报错:
```haskell
Prelude> div 1.2 3
:31:1:
No instance for (Fractional a0) arising from a use of ‘it’
The type variable ‘a0’ is ambiguous
Note: there are several potential instances:
instance Integral a => Fractional (GHC.Real.Ratio a)
-- Defined in ‘GHC.Real’
instance Fractional Double -- Defined in ‘GHC.Float’
instance Fractional Float -- Defined in ‘GHC.Float’
In the first argument of ‘print’, namely ‘it’
In a stmt of an interactive GHCi command: print it
```
#### 3.2.2 构建自己的函数
- doubleMe。doubleMe函数的定义如下:
```haskell
doubleMe x = x + x -- 将数字变为两倍
```
这个函数非常简单,就是返回一个数字的两倍。注意上面函数的定义方式:首先是函数名字,然后是空格分隔的参数,接着是等于号,等于号后面的是函数的实现。要运行这个函数,需要把它写在一个文件里,比如add.hs,然后输入ghci进入命令行模式,输入`:l add.hs`就可以加载函数了。下面是它的调用计算结果:
```haskell
Prelude> :l add.hs
[1 of 1] Compiling Main ( add.hs, interpreted )
Ok, modules loaded: Main.
*Main> doubleMe 2
4
*Main> doubleMe 3
6
```
- **doubleUs**。该函数接收x,y两个参数,然后返回这两个参数之和的两倍。定义如下:
```haskell
doubleUs x y = x*2 + y*2 -- 两个数字变为两倍然后相加
```
调用计算结果为:
```haskell
*Main> doubleUs 1 3
8
*Main> doubleUs 0 2
4
```
- **useDoubleMe**。该函数功能和`doubleUs`一样,只不过其调用了`doubleMe`两次,代码如下:
```haskell
useDoubleMe x y = doubleMe x + doubleMe y -- 调用简单函数
```
对了,忘了说了,haskell中注释是`--`。
- **doubleSmallNumber**。该函数使用`if else`语句实现的功能是:如果x小于100则返回x;否则返回x的两倍。代码如下:
```haskell
doubleSmallNumber x = if x doubleSmallNumber 20
20
*Main> doubleSmallNumber 200
400
```
需要注意的是Haskell中if和else一定是一起出现的,else不可以省略,而且本质上if和else都是需要返回一个值。Haskell中所有的函数和表达式都需要返回一个结果,if语句就是一个表达式,所以它一定会返回一个结果。
- **doubleSmallNumber'**。doubleSmallNumber'函数(注意函数最后有一个`'`)返回的结果是doubleSmallNumber返回值加1,定义为:
```haskell
doubleSmallNumber' x = (if x =100`的时候加1了。另外需要注意的是,Haskell的函数名字开头不能是大写字母。
- **conanO'Brien**。conanO'Brien是一个没有参数的函数,这样的函数也被称为"定义"或者"名字"。conanO'Brien定义如下:
```haskell
conanO'Brien = "It's a-me, Conan O'Brien!" -- 没有参数的函数
```
上面的conanO'Brien函数定义好了之后,`conanO'Brien`就与字符串`"It's a-me, Conan O'Brien!"`等价了,而且字符串的值是不可以修改的。

haskell简明入门(一)的更多相关文章

  1. Jenkins简明入门(三) -- Blue Ocean,让一切变得简单

    我们在上一节Jenkins简明入门(二) 中见识到了Jenkins能做些什么:利用Jenkins完成python程序的build.test.deployment. 同时,也有一种简单的方法,不需要写J ...

  2. OsharpNS轻量级.net core快速开发框架简明入门教程-Osharp.Redis使用

    OsharpNS轻量级.net core快速开发框架简明入门教程 教程目录 从零开始启动Osharp 1.1. 使用OsharpNS项目模板创建项目 1.2. 配置数据库连接串并启动项目 1.3. O ...

  3. OsharpNS轻量级.net core快速开发框架简明入门教程-从零开始启动Osharp

    OsharpNS轻量级.net core快速开发框架简明入门教程 教程目录 从零开始启动Osharp 1.1. 使用OsharpNS项目模板创建项目 1.2. 配置数据库连接串并启动项目 1.3. O ...

  4. OsharpNS轻量级.net core快速开发框架简明入门教程-代码生成器的使用

    OsharpNS轻量级.net core快速开发框架简明入门教程 教程目录 从零开始启动Osharp 1.1. 使用OsharpNS项目模板创建项目 1.2. 配置数据库连接串并启动项目 1.3. O ...

  5. OsharpNS轻量级.net core快速开发框架简明入门教程-基于Osharp实现自己的业务功能

    OsharpNS轻量级.net core快速开发框架简明入门教程 教程目录 从零开始启动Osharp 1.1. 使用OsharpNS项目模板创建项目 1.2. 配置数据库连接串并启动项目 1.3. O ...

  6. DDD简明入门之道 - 开篇

    DDD简明入门之道 - 开篇 犹豫了很久才写下此文,一怕自己对DDD的理解和实践方式有偏差,二怕误人子弟被贻笑大方,所以纰漏之处还望各位谅解.不啰嗦,马上进入正题,如果你觉得此文不错就点个赞吧. 概述 ...

  7. OsharpNS轻量级.net core快速开发框架简明入门教程-Osharp.Hangfire使用

    OsharpNS轻量级.net core快速开发框架简明入门教程 教程目录 从零开始启动Osharp 1.1. 使用OsharpNS项目模板创建项目 1.2. 配置数据库连接串并启动项目 1.3. O ...

  8. OsharpNS轻量级.net core快速开发框架简明入门教程-Osharp.Permissions使用

    OsharpNS轻量级.net core快速开发框架简明入门教程 教程目录 从零开始启动Osharp 1.1. 使用OsharpNS项目模板创建项目 1.2. 配置数据库连接串并启动项目 1.3. O ...

  9. OsharpNS轻量级.net core快速开发框架简明入门教程-切换数据库(从SqlServer改为MySql)

    OsharpNS轻量级.net core快速开发框架简明入门教程 教程目录 从零开始启动Osharp 1.1. 使用OsharpNS项目模板创建项目 1.2. 配置数据库连接串并启动项目 1.3. O ...

随机推荐

  1. 【BZOJ3727】PA2014 Final Zadanie 树形DP

    [BZOJ3727]PA2014 Final Zadanie Description 吉丽YY了一道神题,题面是这样的:“一棵n个点的树,每条边长度为1,第i个结点居住着a[i]个人.假设在i结点举行 ...

  2. 【BZOJ2251】[2010Beijing Wc]外星联络 后缀数组

    [BZOJ2251][2010Beijing Wc]外星联络 Description 小 P 在看过电影<超时空接触>(Contact)之后被深深的打动,决心致力于寻找外星人的事业.于是, ...

  3. ST-LINK使用注意

    利用ST-LINK下载程序注意事项: 1.接线 按照上面图对着自己的开发板连接相应的引脚就可以了. 2.keil5配置 线连接完之后,要对自己的工程进行相关的 配置才能正确进行下载. 首先选择ST-L ...

  4. numeric_limits 模板的相关知识点

    说白了,它是一个模板类,它主要是把C++当中的一些内建型别进行了封装,比如说numeric_limits<int>是一个特化后的类,从这个类的成员变量与成员函数中,我们可以了解到int的很 ...

  5. python中的configparser类

    Python中有ConfigParser类,可以很方便的从配置文件中读取数据(如DB的配置,路径的配置),所以可以自己写一个函数,实现读取config配置. config文件的写法比较简单,[sect ...

  6. 01.MyBatis入门

        MyBatis入参考文档:http://mybatis.org/mybatis-3/zh/  1.使用MyBatis前的准备 1.增加Maven依赖 <dependency> &l ...

  7. Tomcat 500错误 问题集锦

    HTTP 500 - 内部服务器错误  1.jdk版本与Tomcat版本不一样. 问题: 配置一个Web应用的时候,源文件和server.xml.web.xml的配置都没有问题,但是在访问到一个Ser ...

  8. PHP获取目录和的方法通过魔术变量;通过超级全局变量;通过相关函数等等:

    <?php /** * PHP获取路径或目录实现 * @link http://www.phpddt.com */ //魔术变量,获取当前文件的绝对路径 echo "__FILE__: ...

  9. Java char 和 String 的区别: 字符编码及其存储

    一. ASCII码 上个世纪60年代,美国制定了一套字符编码,对英语字符与二进制位之间的关系,做了统一规定.这被称为ASCII码,一直沿用至今.一个字节(8bit)一共 可以用来表示256种不同的状态 ...

  10. 【调研与分析】标杆学习、知识管理和竞争情报的关系——From Team

    注: 1)红色实线箭头表示可以直接使用:例如竞争情报业务输出产品之一的标杆文档可以直接作为公司标杆学习中外部标杆的资料: 2)蓝色虚线箭头表示转化后使用或者间接利用的关系:例如专题调研可以帮助建立内部 ...