本文的主要内容参考自《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. 【BZOJ2668】[cqoi2012]交换棋子 费用流

    [BZOJ2668][cqoi2012]交换棋子 Description 有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态.要求第i行第j列 ...

  2. 【BZOJ1096】[ZJOI2007]仓库建设 斜率优化

    [BZOJ1096][ZJOI2007]仓库建设 Description L公司有N个工厂,由高到底分布在一座山上.如图所示,工厂1在山顶,工厂N在山脚.由于这座山处于高原内陆地区(干燥少雨),L公司 ...

  3. 【BZOJ3312】[Usaco2013 Nov]No Change 状压DP+二分

    [BZOJ3312][Usaco2013 Nov]No Change Description Farmer John is at the market to purchase supplies for ...

  4. 最短路和次短路的条数(dijstra算法或spfa算法)POJ3463

    http://poj.org/problem?id=3463 Sightseeing Time Limit: 2000MS   Memory Limit: 65536K Total Submissio ...

  5. php中的魔术方法(Magic methods)和魔术常亮

    PHP中把以两个下划线__开头的方法称为魔术方法,这些方法在PHP中充当了举足轻重的作用. 魔术方法包括: __construct(),类的构造函数 __destruct(),类的析构函数 __cal ...

  6. .NET截取指定长度字符超出部分以"..."代替

    /// <summary> /// 将指定字符串按指定长度进行剪切, /// </summary> /// <param name= "Str "&g ...

  7. 迁移学习与fine-tuning有什么区别

    假设你要处理一个新数据集,让你做图片分类,这个数据集是关于Flowers的,问题是,数据集中flower的类别很少,数据集中的数据也不多,你发现从零开始训练CNN的效果很差,很容易过拟合,怎么办呢,于 ...

  8. 存储5——逻辑卷管理LVM

    1. LVM概念 LVM是 Logical Volume Manager(逻辑卷管理)的简写,它由Heinz Mauelshagen在Linux 2.4内核上实现.LVM将一个或多个硬盘的分区在逻辑上 ...

  9. Atom预览markdown插件Markdown Preview Enhanced

    atom 上目前最强的 markdown 插件Markdown Preview Enhanced 是一款为 ATOM 编辑器编写的超级强大的 Markdown 插件.这款插件意在让你拥有飘逸的 Mar ...

  10. Spark Streaming带状态更新

    带状态的更新是使用的updateStateByKey方法,里面传入一个函数,函数要自己写,注意需要设置checkpoint import org.apache.spark.streaming.kafk ...