Haskell从零学习读书笔记+参考资料(续中)


通过阅读:

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]

如何从 元组 中把数据取出?

fstsnd可以分别把第一个和第二个数据 取出.

通用技巧是:模式匹配函数式编程的出众特征.

fst的原理:


let first (x,y) = x

元组能嵌套

Haskell文件

Haskell源文件

:load 加载一个hs源码库文件

:reload重新加载

:cd 切换目录

源文件

没有let

源文件里 不用 let?

不能定义同一个量两次
顺序不重要

因为变量不可改变

函数的更多特性

条件表达式

if / then /elseeg:


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

来源:CSDN

原文:https://blog.csdn.net/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) 9mulThree 3 5 9

背后的运行原理:

  1. 把3 交给 mulThree,它返回一个返回函数的函数.

  2. 然后把5交给它.返回一个 取一个参数并使之乘15的函数

  3. 把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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值