Haskell语言学习笔记(1)

Haskell语言

Haskell is a purely-functional, strongly-typed, lazily-executed, expressive higher-level programming language.
Haskell 是一种纯函数型的,强类型的,具有惰性计算特性的,表达能力很强的高级编程语言。所谓纯函数型,是指函数没有副作用。
虽说 Haskell 是小众语言,且相当学术化,但出于以下原因,该语言很有学习的必要。

  • Haskell 语言中存在一些常用语言中缺失的重要概念(比如 typeclasses,Monad 等等)。
  • 一些常用语言中某些语言特性的设计进程(Java 的泛型,C# 的 LINQ,C++ 的 Concept)受到 Haskell 语言及其使用者的强烈影响。

函数

Haskell 语言中的函数具有以下特点:

  • 核心特性。
    分段函数(函数定义):函数定义不必一次完成。结合模式匹配,函数定义可以分多次进行。
    高优先级(函数应用):函数名与参数之间无需括弧,参数之间也无需逗号。
  • 一等公民。函数既可以被当作函数的参数,也可以被当作函数的返回值。把函数作为参数或返回值的函数被称为高阶函数。
  • 纯粹。函数没有副作用。同样的输入必然保证同样的输出。即所谓的引用透明性(referential transparency)。
  • 单参数。技术上讲,Haskell 语言中的函数只能有一个参数,具有多参数的函数在语言中被看成返回函数的函数。也就是说 Haskell 语言中的函数天生就具有柯里化特性。应用部分参数后得到新的函数这一操作被称为部分应用。
  • 惰性求值。计算在需要时才会进行。类似于 C/C++ 的短路效应。有所不同的是,短路效应在 C/C++ 中是特例(仅适用于逻辑运算符),而惰性求值在 Haskell 语言中是常态(如果需要可以用 seq 来规避)。
  • 操作符函数。操作符也是函数。单独使用,使用前缀形式以及部分应用时需加上括弧。
  • 中缀形式。有两个参数的函数具有中缀形式:使用反引号 `(backtick)将函数名括起来的形式。
  • 参数化类型。函数参数的类型可以参数化。相当于 C++ 的函数模板,Java/C# 的泛型方法。
  • 类型自动推导。函数的参数以及返回值的类型可以由编译器(解释器)自行推导。
  • 递归函数。语言中没有循环,尾递归是一种用来代替循环的常见形式。
  • 局部函数。使用 let(前置定义) 或者 where(后置定义) 关键字可以在函数内部定义局部函数。使用 let 时可嵌套,使用 where 时不可嵌套。
  • lambda。也就是匿名函数。
-- map函数的定义
-- 分段函数,参数化类型,模式匹配,尾递归,高阶函数
map :: (a -> b) -> [a] -> [b]
map _ []     = []
map f (x:xs) = f x : map f xs
-- 操作符函数,中缀形式
Prelude> (+3) `map` [1,2,3]
[4,5,6]
-- 部分应用,匿名函数
Prelude> let mapAdd3 = map (\x -> x + 3)
Prelude> mapAdd3 [1,2,3]
[4,5,6]
-- fibs(斐波那契数列)函数的定义
-- 惰性求值,类型自动推导
Prelude> let fibs = 0 : 1 : zipWith (+) fibs (tail fibs) 
Prelude> :t fibs
fibs :: Num a => [a]
Prelude> take 10 fibs
[0,1,1,2,3,5,8,13,21,34]

list comprehension(列表解析)

一种生成list的语法糖,可以表达映射和过滤这两种操作。

-- quicksort(快速排序)函数的定义
-- 参数化类型,类型约束,模式匹配,递归定义,列表解析
quicksort :: (Ord a) => [a] -> [a]
quicksort [] = []
quicksort (x:xs) = quicksort [a | a <- xs, a <= x] ++ [x] ++ quicksort [a | a <- xs, a > x]

data关键字

data关键字可以表达以下概念:

  • structure(结构)相当于 C/C++ 的 struct
  • enumeration(枚举)相当于 C/C++ 的 enum
  • discriminated union(可辨识的联合)其功能相当于但明显优于 C/C++ 中加上类型标签的 union
  • parameterised type(参数化类型)相当于 C++ 的类模板,Java/C# 的泛型类
  • recursive type(递归类型)相当于 C/C++ 中内含指向自身类型的指针成员的结构(比如链表结构)
-- 结构
-- 一个由名,姓,年龄三个字段组成的结构
-- 前面这个Person为类型构造器(type constructor)后面这个Person为值构造器(value constructor)
data Person = Person String String Int
-- 结构(记录语法)
-- 定义结构的同时还给出了读取字段值的函数(相当于字段名)
data Person = Person { firstName :: String, lastName :: String, age :: Int }
-- 枚举
-- 一个由三种颜色组成的枚举类型
data Color = Red | Orange | Yellow
-- 可辨识的联合
-- 一个形状可以是一个由圆心和半径定义而成的圆,也可以是一个由左上角和右下角定义而成的矩形。
data Shape = Circle (Double, Double) Double
           | Rectangle (Double, Double) (Double, Double)
-- 参数化类型,递归类型
-- 一个(二叉)树可以是一颗空树,也可以是一个带有两颗(二叉)子树的节点
-- 节点数据的类型不限,标记为 a 类型
data Tree a = EmptyTree | Node a (Tree a) (Tree a)

(.)运算符和($)运算符

(.)运算符是函数合成(function composition)运算符。
($)运算符是函数应用(function application)运算符。

-- 函数合成运算符
(.) :: (b -> c) -> (a -> b) -> a -> c
(f . g) x = f (g x)
-- 函数应用运算符
($) :: (a -> b) -> a -> b
f $ x = f x

示例

-- 求所有小于10000的奇平方数的和
-- 括弧版本
Prelude> sum (takeWhile (<10000) (filter odd (map (^2) [1..])))
166650
-- 使用函数合成运算符以及函数应用运算符的版本
Prelude> sum . takeWhile (<10000) . filter odd . map (^2) $ [1..]
166650
-- 仅使用函数应用运算符的版本
Prelude> sum $ takeWhile (<10000) $ filter odd $ map (^2) $ [1..]
166650

data, type, newtype关键字

  • data 用于定义新类型。
  • type 用于定义类型的别名。新类型与源类型可互换使用。相当于C/C++的 typedef,C++11 的 using。
  • newtype 用于封装现有类型。新类型只能包含一个构造器和一个字段,字段类型与源类型相同。可以说是不透明的 typedef。
-- 定义新类型
Prelude> data Pair a b = Pair (a, b)
-- 定义类型的别名
Prelude> type Pair a b = (a, b)
-- 封装现有类型
Prelude> newtype Pair a b = Pair { getPair :: (a, b) }

typeclass关键字

HaskellC++C#Java8
typeclassconceptinterface加扩展方法interface加缺省实现

typeclass(类型类)具有以下特点

  • 类型的类型(或者说类型的种类),可以为参数化类型添加类型约束。
    从类型类本身出发,由于类型类代表了一串类型,所以它可以给这些类型加上所属种类的标签(对参数化类型来讲就是约束)。
  • 类似于静态接口,可用于实现混入(mixin)功能,相当于行为继承。
    从所属某个类型类的类型出发,由于类型类的成员函数可以有缺省实现,所以声明某些类型属于某个类型类实质上是给这些类型添加了功能。
  • 可用于实现函数重载。
    从类型类的成员函数出发,由于这些函数针对不同的类型可以有不同的实现,所以类型类实质上为语言增加了函数重载的功能。
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值