通过阅读:
https://zh.wikibooks.org/wiki/Haskell/列表和元组
https://www.kancloud.cn/kancloud/learnyouahaskell/44654 Haskell趣学指南
https://rwh.readthedocs.io/en/latest/index.html RealWorld中文版
我分别学习了wikibooks和Haskell趣学指南.
所有的代码我都做了实践.并且修复了部分原文中的BUG笔误.
本笔记可作为辅助学习用.
Haskell学习笔记 -wikibooks
注意这只是笔记,并不能完全代替原教程学习.
基本
ghci
是Haskell 的交互式命令行
退出方式是:q
或者:quit
变量和函数
let number= 1234
in xxx
函数声明和调用:
即let 函数名 参数列表 = 运算
let func1 firstArgv secondArgv = firstArgv+secondArgv
func1 1 2 //结果是3
列表与元组
列表
note that: List中的List可以 长度不同,但必须得是 相同类型.
[[1,2,3],[1]] //合法
定义一个列表
let numbers = [1,2,3,4];
将数据附加到列表中使用: cons
操作符eg:
1234:numbers //结果是 [1234,1,2,3,4]
结果还是列表所以复合用法是:
let x = [0]
1:3:2:x //结果是 [1,3,2,x]
实际上所有的列表都是在一个空的列表[]
的基础上附加数据创建的:即以下相等:
[1,2,3,4,5]
1:2:3:4:5:[]
列表能包含列表.
元组
把多个值存储到一个值的方法.
可用于返回值,避免了某些语言 到处临时定义结构的麻烦:
(True,1)
("Hi",Flase,4,5)
n个元素叫n-tuple.
元组用于函数返回多个值.
不同类型顺序和数量的元组 视为 类型不同.
[1,"a"]
["a",1]
如何从 元组 中把数据取出?
fst
和 snd
可以分别把第一个和第二个数据 取出.
通用技巧是:模式匹配
函数式编程的出众特征.
fst
的原理:
let first (x,y) = x
元组能嵌套
Haskell文件
Haskell源文件
:load
加载一个hs源码库文件
:reload
重新加载
:cd
切换目录
源文件
没有let
源文件里 不用 let?
不能定义同一个量两次
顺序不重要
因为变量不可改变
函数的更多特性
条件表达式
if / then /else
eg:
mySignum x =
if x < 0 then
-1
else if x > 0 then
1
else
0
测试方法:
mySignum(-1)
mySignum 2
mySignum 0
case
除了在 模式匹配中 拥有强大功能外,还可以 替代 if 改善可读性.
fcase x =
case x of
0 -> 1
1 -> 5
_ -> (-1)
注意缩进非常重要.
使用一个layout
的布局系统对程序的代码进行维护.
缩进
分号和花括号 的方式 可以代替以上的缩进:
f x = case x of
{0->1;1->5;_->(-1)}
为不同参数定义一个函数
分段定义方法:
f 0 = 1
f 1 = 5
f _ = -1 //注意顺序
函数合成
square (f 1)
.
函数,函数的合成函数.将两个函数合成为一个函数.
(square . f)
表示一个有参数的函数,先把这个参数代入函数f
,再把结果代入函数square
.
fa a = a+1
fb b = b+1
bc c = c+1
(fa . fb . fc) 0 //相当于fa(fb(fc 0))
Prelude 中 定义了很多函数,所以用前可以看看不用自己实现了.
let绑定
let 语句可以同时声明多个值,只要保持相同缩进:
roots a b c =
let disc = sqrt(b^2 - 4*a*c)
twice_a = 2*a
in ((-b + disc) / twice_a,
(-b - disc) / twice_a)
局部绑定可以解决函数中重复的问题:let/in
// 这个用法需要更深入了解
快速排序
quicksort [] = []
quicksort (p:xs) = (quicksort lesser) ++ [p] ++ (quicksort greater)
where
lesser = filter (< p) xs
greater = filter (>= p) xs
q_sort n=case n of
[]->[]
(x:xs)->q_sort [a|a<-xs,a<=x]++[x]++q_sort [a|a<-xs,a>x]
// //(x:xs)-->把传递的数组结构分成首项+剩余两个部分 同时作为参数传递到后面的函数中,首项的作用就是上面说的a啦,当然也可以时最后一项,这里取首项时方便一些
//[a|a<-xs,a<=x] 这里是什么意思呢?a|,这个a代表输出,可以理解为返回值,a<-xs,a<=x表示这个a从xs中来,同时a小于x,也就是传进来的去掉第一项的数组,输出小于等于x的数组元素,综合来看,这句话实现了:把小的数组元素扔左边的功能;
---------------------
作者:gohike
来源:优快云
原文:https://blog.youkuaiyun.com/gohike/article/details/52719409
版权声明:本文为博主原创文章,转载请附上博文链接!
列表推导
我们可以像描述数学问题一样来描述一个列表表达式: 结果是个整数.
> [x*2| x<- [1..10]]
[2,4,6,8,10,12,14,16,18,20]
获得 100 以内 所有6的倍数, 通过列表推导 快速获得:
>[x | x <- [1..100],mode x 6 == 0]
计算直角三角形的问题:斜边的长度为 1-10 之间的整数,求可能的三角形边长。
> [(a,b,c)| a<- [1..10],b <- [1..a],c<-[1..b],b^2+c^2==a^2]
[(5,4,3),(10,8,6)]
另一个例子:
[a|b<-[1..120],a<-[b^2]]
规则推测是: [结果|条件式子一们]
<-
符号来自于.
趣学入门 - 教有其他语言基础的人怎么学
https://www.kancloud.cn/kancloud/learnyouahaskell/44654 Haskell趣学指南
第二章 入门
函数 首字母不能大写,标识符可以用'
号来表达,经过略微修改的函数.
没有参数的函数通常被称为定义
List入门
head [1,2,3,4]
tail //返回除头外
last //尾
init //返回除了尾外的List
length
List 有自己的一堆函数:
take x// 返回前x个元素
reverse //翻转
maximum
minimun
null //检查空
sum
product //所有元素的积
elem //判断元素是否存在其中,常以中缀方式出现
elem 4 [1,2,3,4]
的中后缀表达是: 4 `elem` [1,2,3,4]
用撇号来包含住函数.
List合并: 使用 ++
运算符.注意数据太大时,因为遍历整个List,会慢,好的办法是在前面直接加数据:
5:[1,2,3,4]
字符串实际上是一组字符的list语法糖.以下等同
> ['h','i']++" how are you"
"hi how are you"
length函数:
my_length xs = sum [1| _<-xs]
区间Range
[1…3]和[1,2,3]完全等价.
[‘a’…‘z’]字母也可以提供区间
还允许你声明步长
[2,4…20] // 后面的20是结束,只会根据2,4的规律来.
[2,4…21] //结果一样.
避免在Range内使用浮点数,因为浮点数是不精确的.可能推测出0.8999999之类.
[13,26…] //可以不标明上限,获得无限长的List,按需生成,
take 10 [13,26]//从中获取10个数字
cycle
可以将提供的List参数返回一个无限长的List
repeat
将一个值输入返回一个无限List
take 5 repeat 4 // [4 ,4 ,4 ,4 ,4]
或者用replicate
获取指定数量的重复:
replicate 3 4//[4,4,4]
集合 Comprehension
集合表示: S= {2*x | x<-N,x<=10}
**|**左是输出函数,x是变量,N是输入集合
再添加个限制条件:要求x*2>=12
:
[x*2| x<- [1..10],x*2>=12]
一个从List中筛选的过程叫做:filtering过滤.
boomBangs xs = [if x<10 then “BOOM!” else “BANG!”| x xs, odd x]
//odd是限制是否奇数
**=**表示不等于.
另一个例子:
[x*y| x<-[1,2,3],y<-[1,2]]//最终结果会是6个.
以下是从嵌套List中找偶数,且不需要拆开嵌套的List.用法如下:
let xxs = [[1,2,3,4]]
[[x| x<-xs ,even x]| xs <- xxs]
Tuple 元组
以下函数只能用于二元组
fst (1,2)
snd (1,2)
zip [1,2] [3,4] //可用与生成一组二元List,常用在需要组合或是遍历两个List时.如下是个例子:
[(1,3),(2,4)]//一一对应做组合
函数式编程的一般思路:
先取一个初始的集合并将其变形,执行过滤条件,最终取得正确的结果。
第三章 类型和类型类
与Java相比,haskell支持类型推导.
这样我们就不必在每个函数和表达式上都表明其类型了.
:t 表达式
可以检测表达式的类型在ghci.
Char,Bool,[Char] //一组字符的List
(在文件中)声明类型是个好习惯,方法是::
最后一个Int是返回值的类型:
addThree :: Int -> Int -> Int -> Int
addThree x y z = x + y + z
Int:整数有范围限制:
对32位的机器而言,上限一般是214748364,下限是-214748364。
Integer:是无界的.想多大就多大.但效率<Int.
Float 单精度的浮点数
Double 双精度的浮点数
类型变量
类型变量可以让我们轻而易举的写出类型无关的函数.
使用类型变量 的函数被称作"多态函数".
ghci> :t fst
fst :: (a, b) -> a // 类型必须是首字母大写,它是小写,代表它是任意类型
类型类
类型类 可以理解为接口.
ghci> :t (==)
(==) :: (Eq a) => a -> a -> Bool
其中 => 为类型约束
取两个相同类型的值作为 作为参数 并返回一个Bool,这两参数的类型同在 Eq之中 (即类型约束)
Eq这一类型类 提供了 判断 相等性的接口,反是可比较相等性的类型 必属于Eq类
另一个例子:
elem函数的类型为: (Eq a)=>a->[a]->Bool
因为它内部使用了==的缘故.
几个基本的类型类
- Eq
可判断相等性的类型,提供实现的函数是**==** 和 /=.
除函数以外的所有类型都属于Eq类,都可以判断相等性。
- Ord
包含可比较大小的类型,除函数以外,结果是三种类型之一: GT,LT,EQ
compare 1 2
- Show
它的成员为可用字符串表示的类型,除函数以外,
操作Show类型类,常用函数**show **取任一Show的成员类型并将其转为字符串.
show 3 //"3"
show True //"True"
- Read
与Show类型类相反,将一个字符串转为Read的某成员类型:
其中常用函数 read:
read "3.1" + 3.8 //6.9
read "[1,2,3,4]" ++ [3] // [1,2,3,4,3]
read必须推断出要什么类型,虽然编译器可以辨认出 大部分 表达式的 类型:
read "3" //提示错误,它不知道要转换成哪种类型
read "3"::Float //正确
- Enum
成员必须都是连续的类型(可枚举的).
好处: 可在Range中用到它成员类型,每个值都有:前一个 successer 和 后一个 predecesor.
可用succ和pred函数得到.
succ 'B' //'C'
pred 'b' // 'a'
(),Bool,Char,Ordering,Int,Integer,Float和Double。
- Bounded
它的成员,都有一个上限和下限.
minBound :: Char // "\Nul"
maxBound :: Bool // "True"
- Num
包含实数和整数
-
Integral
-
Floating
fromIntegral
函数 处理数字非常有用:
其类型声明为:
fromIntegral :: (Num b, Integral a) => a -> b
取一个整数做为参数,返回一个更加通用的数字.
fromIntegral (length [1,2,3,4]) + 3.2 // 直接加3.2不行…历史原因…
第四章 函数的语法
模式匹配
通过检查数据 的 特定结构 来检查其 是否匹配,并 按模式 从中取得数据.
模式匹配 使 我们避免了一颗大的 if-else树.
sayMe :: (Integral a) => a -> String
sayMe 1 = "One"
sayMe x = "No 1"
阶乘的玩法:
fac :: (Integral a) => a -> a
fac 0 = 1
fac n = n* fac(n-1)
就想 sayMe x一样.
因此,在定义模式时,一定要留一个万能匹配的模式,这样我们的程序就不会为了不可预料的输入而崩溃了。
list本身也可以使用模式匹配,你可以用 []
或 :
来匹配
因为[1,2,3]
在表达1:2:3:[]
的语法糖
x:xs <- [1,2,3]
//这种模式 可以将 list的头部绑定为x,其他部分绑定为xs
其中x为[1],xs为[2,3].
绑定多个变量怎么处理?
x:y:z:xs
此时就获取xs的前三个变量.
实现个自己的head函数:
head_ :: [a] -> a
head_ [] = error "dummy!"
head_ (x:_) = x
对于固定长度的List,eg:
(x:[ ]) 和(x:y:[ ]) 可以写为 [x]和[x,y]
length' :: (Num b) => [a] -> b
length' [] = 0
length' (_:xs) = 1 + length' xs
as模式
as模式是为了 在较大的 模式中保留对整体的引用,从而减少重复性的工作:
capital :: String -> String
capital "" = "Empty string, whoops!"
capital all@(x:xs) = "The first letter of " ++ all ++ " is " ++ [x]
-- capital (x:xs) = "The first letter of " ++ x:xs ++ " is " ++ [x]
看上面两条是等效的.但是第1条更简约.
门卫 guard
模式用来 检查一个值是否合适并从中取值.
Guard用来检查一个值的某项 属性是否为真. 像if,但是可读性更高,与模式匹配契合的很好.
门卫由 函数名和参数后面的竖线**|**表示,每个门卫都是一个布尔表达式,真就执行相应函数体.
bmiTell bmi
| bmi<18.5 = "瘦"
| bmi<25.0 = "正常"
| bmi<30.0 = "超重"
| otherwise = "肥胖"
其中otherwise = true
.
Where
where
是紧跟在函数体底部定义 的用来定义 多个名字和函数 的 方法.(局部变量)
对门卫可见.
bmiTell weight height
| bmi<18.5 = "瘦"
| bmi<normal = "正常"
| bmi<30.0 = "超重"
| otherwise = "肥胖"
where bmi = weight /height ^ 2
normal = 25.0
另一个获取姓名的首字母的函数 使用where和模式匹配的例子:
initials :: String -> String -> String
initials firstname lastname = [f] ++ ". " ++ [l] ++ "."
where (f:_) = firstname
(l:_) = lastname
where 可以嵌套,一种广为接受的理念:
一个函数应该有几个辅助函数,而每个辅助函数也可以通过where拥有各自的辅助函数.
Let
-
允许在任何位置定义 局部变量 或 局部函数
-
对不同的门卫不可见
-
是个表达式,比where要灵活自由
let [绑定] in [表达式]
,绑定的名字仅对in部分可见.
(let a = 100; b = 200; c = 300 in a*b*c, let foo="H"; bar = "i" in foo ++ bar) // (6000000,"Hi")
在let绑定的前面无法使用它.因为还没有定义…
let是个表达式,定义域限制的相当小,因此不能在多个门卫中使用.
所以我们 还需要where的.
case表达式
模式匹配 本质上 不过就是case语句的语法糖而已,以下代码完全等价:
head' :: [a] -> a
head' [] = error "No head for empty lists!"
head' (x:_) = x
--
head' :: [a] -> a
head' xs = case xs of [] -> error "No head for empty lists!"
(x:_) -> x
语法如下:
case expression of pattern -> result
pattern -> result
pattern -> result
...
另外一个例子是:
describeList :: [a] -> String
describeList xs = "The list is " ++ case xs of [] -> "empty."
[x] -> "a singleton list."
xs -> "a longer list."
也可以写成 where 语法: (其中what 是我们自己定义的函数)
dL :: [a] -> String
dL xs = "Is " ++ what xs
where what [] = "Empty"
where [x] = "Singleton list"
where xs = "a longer list"
That Tips: 错误的缩进会导致错误
qian_xin' moun =
case moun of 12 -> "1"
1 -> "2"
2 -> "2"
3 -> "3"
4 -> "4"
moun -> error "不考虑"
case of 的选项如果 ,12 ,1 ,2, 3, 4 ,moun不在同一排上就会提示以下:
Unexpected case expression in function application:
case moun of
12 -> "1"
1 -> "2"
2 -> "2"
3 -> "3"
4 -> "4"
You could write it with parentheses
Or perhaps you meant to enable BlockArguments?
|
103 | case moun of 12 -> "1"
| ^^^^^^^^^^^^^^^^^^^^^^^^...
Failed, no modules loaded.
第五章 递归
斐波那契数列:
fib 0=0
fib 1=2
fib num = fib (num-1) + (fib num-2)
边界条件对于递归至关重要.
命令式语言要求你提供 求解的步骤
Haskell 倾向于让你 提供 问题的描述
所以没有while和for循环的原因. 递归是我们的方案.
求列表最大
maximum'' [] = error "empty list!"
maximum'' [x] = x
maximum'' (x:xs)
| x > maxTail = x
| otherwise = maxTail
where maxTail = maximum'' xs
maximum' [] = error "empty list!"
maximum' [x] = x
maximum' (x:xs) = max x (maximum' xs)
先定义边界条件
最定义递归过程
几个递归函数
自己定义一个replicate
重复 time 个 num在列表中:
以下是两个例子:
replicate' time num
|time >0 = num:(replicate' (time-1) num)
|time == 0 = []
|otherwise = error ("error: <0")
replicate' :: Int->Int->[Int]
replicate' num (0) = []
replicate' num time>2 = num:(replicate' num (time-1))
自定义一个take
这里用到的技巧是分情况处理,而不局限于一个函数中:
take' :: (Num i,Ord i) => i->[a]->[a]
take' n m
| n<=0 = []
|(null m) = error "No enough!"
take' n (x:xs) = x:take'(n-1) xs
另一个版本是教程的:
take' :: (Num i,Ord i) => i->[a]->[a]
take' n _
| n <= 0 = []
take' _ [] = []
take' n (x:xs) = x : take' (n-1) xs
特点:是其未使用null来辅助判空,而是直接 模式匹配用 []来处理.
模式匹配是关键.!
自定义一个库函数 elem
该函数用来检查是否属于包含关系:
我实现的版本:
elem' :: Ord a=> a-> [a] -> Bool
elem' _ [] = False
elem' n (x:xs)
| n==x = True
| otherwise = elem' n xs
作者实现的版本更好的限制了类型:
elem' :: (Eq a) => a -> [a] -> Bool
elem' a [] = False
elem' a (x:xs)
| a == x = True
| otherwise = a `elem'` xs
简单直接. 若头部不是该元素, 就检测尾部, 若为空List就返回False.
排序-快排
我用where做:
qsort'' :: Ord a=> [a]->[a]
qsort'' []=[]
qsort'' (x:xs) =
small ++ [x] ++ big
where
small = qsort''([a|a<-xs,a<=x])
big = qsort''([a|a<-xs,a>x])
let in 来做:
qsort'' :: Ord a=> [a]->[a]
qsort'' []=[]
qsort'' (x:xs) =
let
small = qsort''([a|a<-xs,a<=x])
big = qsort''([a|a<-xs,a>x])
in
small ++ [x] ++ big
我认为其他语言用栈做这个事件似乎比较合适.
我们已经递不少归了,也许你已经发觉了其中的固定模式:先定义一个边界条件,再定义个函数,让它从一堆元素中取一个并做点事情后,把余下的元素重新交给这个函数。 这一模式对List、Tree等数据结构都是适用的。
再者就是边界条件。一般而言,边界条件就是为避免程序出错而设置的保护措施,处理List时的边界条件大部分都是空List,而处理Tree时的边界条件就是没有子元素的节点。
使用递归来解决问题时应当先考虑递归会在什么样的条件下不可用, 然后再找出它的边界条件和单位元, 考虑参数应该在何时切开(如对List使用模式匹配), 以及在何处执行递归.
第六章 高阶函数
柯里函数
好熟悉的名字,柯里化…
本质上,haskell所有函数都只有一个参数.
所有多个参数的函数都是 柯里函数
max 4 5
(max 4) 5
mulThree x y z = x*y*z
((mulThree 3) 5) 9
((mulThree 3) 5) 9
或mulThree 3 5 9
背后的运行原理:
-
把3 交给 mulThree,它返回一个返回函数的函数.
-
然后把5交给它.返回一个 取一个参数并使之乘15的函数
-
把9交给这一函数,返回135.
想想,这个函数的类型也可以这样写:
mulThree:: (Num a) => a -> (a -> (a -> a))
->
前面的东西就是函数取的参数,后面的东西就是其返回值.所以说,我们的函数取一个a,并返回一个类型为(Num a)=>a ->( a->a)的函数.如此递推.
以不全的参数调用函数可以方便创造新的函数.
compareWithHundred = compare 100
compare 100会返回一个取一数与100比较的函数.
不全调用在中缀运算中的用法:
divTen = (/10) //加括号即可
(/10) 200,和 200/10 等价.
注意,返回的函数是无法打印的,因为函数不是Show 类型的实例:
会显示 类似以下错误:
<interactive>:16:1: error:
? No instance for (Show (Integer -> Integer))
arising from a use of ‘print’
(maybe you haven't applied a function to enough arguments?)
? In a stmt of an interactive GHCi command: print it
高阶函数
函数是一等公民,所以,取一个函数做为参数和返回值.
例子是一个函数 applyTwice 接收一个函数,并调用它两次.
Note that: **->**具有右结合性,所以需要一个括号来表明这是一个函数.
applyTwice:: (a->a) -> a -> a
applyTwice f a= f(f(a))
applyTwice (3:) []
applyTwice ("Hi " ++) "L"
-- "Hi Hi L"
applyTwice (++ "Hi") "L"
-- "L Hi Hi"
以上是不全调用的展示,
我的理解: 不全调用函数可以被串连使用等,就像普通的函数.
如果有个函数要我们给它传一个一元函数,可以不全调用一个函数让它剩一个参数,再把它交出去.
这是作者认为神奇的点.
实现zipWith库函数
zipWith 有三个参数: 一个函数,两个List,结果是一个List.
用两个List中的同一个序号的项传给函数,结果得到一个项。
zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith' _ [] _ = []
zipWith' _ _ [] = []
zipWith' f (x:xs) (y:ys) = f x y : zipWith' f xs ys
其中 f x y : zipWith' f xs ys
的语法就是普通的 将一个元素放入列表中…
一个简单的的高阶函数可以玩出很多花样.
命令式语言使用for,while,赋值,状态检测来实现功能,再包起来留个接口,使之像个函数一样调用.
函数式语言使用高阶函数来抽象出常见的模式,像成对遍历并处理两个List或从中筛掉自己不需要的结果.
实现flip库函数
flip能实现将参数给换个个.返回结果依然是个函数
flip':: (a -> b -> c) -> (b -> a -> c)
flip' f a b = f b a
//这里体验了柯里函数的优势,只要调用flip' f,而不加参数即可获得 新函数.
实现map和filter
map用途广大.
map(map (^2)) [[1,2],[3,4]] //[[1,4],[9,16]]
map (++ "!") ["Hi","How","Ok"]
map 的功能都可以用List Comprehension来替代:
[a++"!"|a<-["Hi","How","Ok"]]
filter 是 传入一个判断函数,和一个列表,用每一项调用判断函数.
如果True就添加到返回列表中
filter' :: (a->Bool) -> [a] -> [a]
filter' _ []=[]
filter' f (x:xs)
| f x = x:filter' f xs
| otherwise = filter' f xs
filter (==3) [1,2,3,4,5,6]
同样可以用List Comprehension方法:
[a|a<-[1,2,3,4,5,6],a==3]
我发现递归似乎是一个从底向顶展开的过程.
作者建议: map
, filter
或者 List Comprehension
哪个舒服用哪个.
以下是快排的filter版本:
quicksort :: (Ord a) => [a] -> [a]
quicksort [] = []
quicksort (x:xs) =
let smallerSorted = quicksort (filter (<x) xs)
biggerSorted = quicksort (filter (>x) xs)
in smallerSorted ++ [x] ++ biggerSorted
takewhile
函数 它取一个限制条件和List做参数.
返回符合限制条件的元素,一遇到不符合条件的元素,它就结束了.
求所有小于10000的奇数的平方的和:
sum (takeWhile (<10000)(filter odd (map (^2)[1..])))
在大于10000处将它断开.结果是:166650
haskell的惰性使得其可以在 延迟在任何时间进行断开.
而不是 一开始就必须设定范围.
取List第X个元素!! x
:
[0,1,2]!!2 //结果是2
运用柯里函数和不全调用 函数 可以创造函数列表:
以下内容写在ghci控制台即可:
ghci> let listOfFuns = map(*)[0..]
ghci> (listOfFuns !! 4) 5
20
和4*5等价.
实现lambda
对于只用一次的函数 可以 使用labmda.
在Haskell中lambda匿名函数的表示方法是\
开头的函数.
如果不希望整个行被识别为 lambda可以用括号将其括起来.eg:
numLongChains :: Int
numLongChains = length (filter (\xs = length xs > 15) (map chain [1..100]))
其中xs = length xs> 15
就是lambda.
不熟悉柯里函数与不全调用的人们往往会写出很多lambda,而实际上大部分都是没必要的。例如,表达式map (+3) [1,6,3,2]与map (\x -> x+3) [1,6,3,2]等价,(+3)和(\x -> x+3)都是给一个数加上3。不用说,在这种情况下不用lambda要清爽的多。
lambda中无法为一个参数设置多个模式,如[] 和 (x:xs)同时设置
lambda的模式匹配如果失败,就会引发运行时错误,谨慎使用!
以下的等价内容将后面的整个语句做为lambda的函数体,更能说明柯里化.
addThree :: (Num a) => a -> a -> a -> a
addThree x y z = x + y + z
addThree :: (Num a) => a -> a -> a -> a
addThree = \x -> \y -> \z -> x + y + z
折叠纸鹤
有$的函数调用
函数组合
模块
构造我们自己的类型和类型类
待续
疑问
? 能否实现一个四则计算器?
?能否方便实现各种parse?
?进制转换?生存
?支持正则吗?
?调用API?
计划
做高级编译器 -> 其他源码阅读 -> 操作系统
-> TCP/IP协议详解卷一
https://www.kancloud.cn/lifei6671/tcp-ip
接下来做什么呢?
指得是当达到Haskell的目标后
网络真是吸引人的.TCP/IP协议详解卷一,将是我们的下一个目标,
理由是我们接下来的发展方向是偏网络, 而这是基础.
这将会大幅增强我们的网络技能.
同样它是其他源码阅读的基础
XXXX
|
TCP/IP