0%

Haskell 基础知识

LISP 基础知识

LISP 是一种通用高级计算机程序语言,长期以来垄断人工智能领域的应用

LISP 作为应用人工智能而设计的语言,是第一个声明式系内函数式程序设计语言,有别于命令式系内过程式的 C,Fortran 和面向对象的 Java,Python 等结构化程序设计语言

数据类型

LISP 只有两种数据结构:

原子 atom:原子为标识符形式的符号或数字的字面值

表 list:表则是由零个或多个表达式组成的序列(不需要使用一般表处理所必需的任意插入及删除操作)

语句结构

LISP 的语法是简洁的典型,程序代码与数据的形式完全相同

以圆括号为边界的表:(A B C D)

  • 数据:有 A,B,C,D 这4个元素的表
  • 函数:将名为 A 的函数作用于 B,C 和 D 这3个参数

嵌套表结构亦是以圆括号来表示:(A(B C)D(E(F G)))

关键字

LISP 是一个函数式程序语言,并无关键字或保留字设,使用者可自行定义

Haskell 基础知识

Haskell 是一种函数式编程语言,专门设计用于处理符号计算和列表处理应用程序,有非限定性语义和强静态类型

数据类型

Haskell 支持的类型如下:

  • 数字:Haskell 可以将某些数字解码为数字(无需像在其他编程语言中通常那样在外部指定它的数据类型)
  • 字符:双引号或单引号中的单个字符
  • 字符串:双引号中的多个字符
  • 布尔型:True / False
  • 列表:用逗号分隔的相同数据类型的集合
  • 元组:在单个数据类型中声明多个值的方法(元组可以视为列表,但是元组和列表之间存在一些技术差异)

函数式编程

函数式编程是一种编程范式,除了函数式编程之外还有:命令式编程,声明式编程

命令式编程

命令式编程是面向计算机硬件抽象(变量、赋值语句、表达式、控制语句等)的编程

可以理解为命令式编程就是冯诺伊曼的指令序列,它的主要思想是关注计算机执行的步骤,即一步一步告诉计算机先做什么再做什么

例如:C语言中,要查找数组 numList 中大于5的所有数字

1
2
3
4
5
6
let results = [];
for(let i = 0; i < numList.length; i++){
if(numList[i] > 5){
results.push(numList[i])
}
}

声明式编程

声明式编程是以数据结构的形式来表达程序执行的逻辑

它的主要思想是告诉计算机应该做什么,但不指定具体要怎么做

例如:SQL语句

1
SELECT * FROM collection WHERE num > 5

函数式编程

函数式编程和声明式编程的思想是一致的:只关注做什么而不是怎么做

但函数式编程不仅仅局限于声明式编程

函数式编程是面向数学的抽象,将计算描述为一种表达式求值,其实,函数式程序就是一个表达式

  • 函数式编程中的函数并不指计算机中的函数,而是指数学中的函数(即自变量的映射)
  • 函数的值取决于函数的参数的值,不依赖于其他状态(例如:abs(x) 函数计算 x 的绝对值,只要 x 不变,最终的值都是一样)

函数是第一等公民:是指函数跟其它的数据类型一样处于平等地位,可以赋值给其他变量,可以作为参数传入另一个函数,也可以作为别的函数的返回值

1
2
3
4
5
6
7
8
9
var func1 = function func1() {  } /* 赋值 */

function func2(fn) { /* 函数作为参数 */
fn()
}

function func3() { /* 函数作为返回值 */
return function() {}
}

函数是纯函数:纯函数是指相同的输入总会得到相同的输出,并且不会产生副作用的函数(函数内部的操作不会对外部产生影响)

函数式编程的基本运算

函数合成(compose):

  • 将代表各个动作的多个函数合并成一个函数

案例一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function add(x) { /* 分散的多个函数 */
return x + 10
}
function multiply(x) {
return x * 10
}

function compose(f,g) { /* 合成函数 */
return function(x) {
return f(g(x));
};
}

console.log(multiply(add(2))) // 120
let calculate=compose(multiply,add); /* 执行顺序:从右往左 */
console.log(calculate(2)) // 120

案例二:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function compose() {
let args = arguments;
let start = args.length - 1;
return function () {
let i = start - 1;
let result = args[start].apply(this, arguments);
while (i >= 0){
result = args[i].call(this, result);
i--;
}
return result;
};
}

function add(str){
return x + 10
}
function multiply(str) {
return x * 10
}
function minus(str) {
return x - 10
}

let composeFun = compose(minus, multiply, add); /* 执行顺序:从右往左 */
composeFun(2) // 110

函数柯里化(Currying):

  • 一个柯里化的函数首先会接受一些参数,接受了这些参数之后,该函数并不会立即求值,而是继续返回另外一个函数
  • 刚才传入的参数在函数形成的闭包中被保存起来,待到函数被真正需要求值的时候,之前传入的所有参数都会被一次性用于求值

案例一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function sum(a, b) {
return a + b;
}
console.log(sum(2, 2)) // 4

function sumCurry(a) {
return function(b) {
return a + b;
}
}
console.log(sumCurry(2)(2)); // 4

var sumCurry=createCurry(sum); /* 函数柯里化 */
console.log(sumCurry(2)(2)); // 4

案例二:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function createCurry(func, arrArgs) { /* 参数只能从左到右传递 */
var args = arguments;
var funcLength = func.length;
var arrArgs = arrArgs || [];

return function() {
var _arrArgs = Array.prototype.slice.call(arguments);
var allArrArgs = arrArgs.concat(_arrArgs)

if (allArrArgs.length < funcLength) { /* 如果参数个数小于最初的func.length,则递归调用,继续收集参数 */
return args.callee.call(this, func, allArrArgs);
}

return func.apply(this, allArrArgs); /* 参数收集完毕,则执行func */
}
}

var sumCurry=createCurry(function(a, b, c) { /* 返回一个柯里化函数 */
return a + b + c;
});
sumCurry(1)(2)(3) // 6
sumCurry(1, 2, 3) // 6
sumCurry(1)(2,3) // 6
sumCurry(1,2)(3) // 6

高阶函数:满足下列条件之一的函数就可以称为高阶函数

  • 函数作为参数被传递
  • 函数作为返回值输出

案例一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const fn = (() => {
let students = [];
return { /* 作为返回值输出 */
addStudent(name) {
if (students.includes(name)) {
return false;
}
students.push(name);
},
showStudent(name) {
if (Object.is(students.length, 0)) {
return false;
}
return students.join(",");
}
}
})();
fn.addStudent("liming");
fn.addStudent("zhangsan");
fn.showStudent(); // 输出:liming,zhangsan

案例二:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const plus = (...args) => {
let n = 0;
for (let i = 0; i < args.length; i++) {
n += args[i];
}
return n;
}

const mult = (...args) => {
let n = 1;
for (let i = 0; i < args.length; i++) {
n *= args[i];
}
return n;
}

const createFn = (fn) => { /* 作为参数被传递 */
let obj = {};
return (...args) => { /* 作为返回值输出 */
let keyName = args.join("");
if (keyName in obj) {
return obj[keyName];
}
obj[keyName] = fn.apply(null, args);
return obj[keyName];
}
}

let fun1 = createFn(plus);
console.log(fun1(2, 2, 2)); // 输出:6

let fun2 = createFn(mult);
console.log(fun2(2, 2, 2)); // 输出:8

GHC - Haskell 编译器

GHC 就是 Haskell 的编译器,可以从如下网站中下载:

执行以下代码就可以完成安装:

1
2
sudo ./configure
sudo make install