1, 何为函数
The behavior of functions in Haskell comes directly from mathematics. In math, we often say things like f(x) = y, meaning there’s some function f that takes a parameter x and maps to a value y. In mathematics, every x can map to one and only one y. If f(2) = 2,000,000 for a given function f, it can never be the case that f(2) = 2,000,001.
haskell中的函数跟数学中的函数一致,所有函数名都以小写字母开头
函数的三个特征:
All functions must take an argument.
All functions must return a value.
Anytime a function is called with the same argument, it must return the same
value.
使用相同的参数调用同一个函数永远返回相同的值,此特征又叫引用透明性
2, 函数式编程
基于lambda演算,最大的特征是不允许产生副作用(side effects),也即是不允许修改程序的状态。
tick()
if(timeToReset) {
reset()
}
这是一段典型的命令式风格的代码,tick和reset都不接受参数,他们起作用的唯一方式是修改全局变量表示的程序状态,也即是副作用。因为函数的行为跟程序状态有关系,所以很难预测某次函数调用的行为。
haskell不允许side effects,不管某个函数来自哪里,在任何情况下调用都可以保证相同的参数得到相同的结果,经过测试的代码行为是安全确定的,大大减少了bug!!!
3,变量
haskell也可以定义变量,定义时必须指定值且以后不允许修改。相当于所有变量都是final / const修饰的。所以变量在haskell中的主要作用是避免重复
calcChange owed given = if given - owed > 0
then given - owed
else 0
这段代码given - owed会计算两次,可以使用where/let定义变量解决此问题
-- expression where <bindings>
calcChange owed given = if change > 0
then change
else 0
where change = given – owed
-- let <bindings> expression
calcChange owed given = let change = given - owed
in if change > 0
then change
else 0
变量在GHCi里是可以重新赋值的,只是为了方便!!!
4,lambda
lambda可以替换where/let中的变量定义
calcChange owed given = (\change ->
if change > 0
then change
else 0) (given - owed)
Whenever you create a new function, named or not, you create a new scope, which is the context in which a variable is defined. When a variable is used, the program looks at the nearest scope; if the definition of the variable isn’t there, it goes to the next one up. This particular type of variable lookup is called lexical scope. Both Haskell and JavaScript use lexical scoping, which is why IIFE and your lambda function variables behave in a similar fashion.
haskell的变量范围跟javascript一致,每个函数创建一个scope,变量定义在scope中,查找变量时从最近的scope开始。
5,函数是first-class可以作为参数或返回值
names = ["Curtis", "Bernard"]
compareName n1 n2 = if n1 > n2
then GT
else if n1 < n2
then LT
else EQ
sortedNames = sort compareName names
函数作为参数:模版方法设计模式
nyOffice name = (fst name) ++ "@New York"
sfOffice name = (snd name) ++ "@San Francisco"
getLocationFunction location = case location of
"ny" -> nyOffice
"sf" -> sfOffice
_ -> (\name -> (fst name) ++ "@null")
函数作为返回值
6,闭包和partial application
add3 a b c = a + b + c
-- 等价于
add3 a b = \c -> a + b + c
partial application:当调用函数时参数少于定义函数时参数,返回一个闭包
7,list:相当于命令式语言的数组,最基础的数据结构
A list is either an empty list or an element followed by another list.
list的本质是每个元素用:连接,最后连接[ ]
构造list:
1 : 2 : 3 : [ ] = [1, 2, 3]
[1 .. 5] = [1, 2, 3, 4, 5]
[1, 3 .. 10] = [1, 3, 5, 7, 9]
[10, 8 .. 1] = [10, 8, 6, 4, 2]
[1, 3 .. ] 忽略界限,创造出延迟计算的无穷队列
String的本质是Char组成的list。
"Hello" = ['H', 'e', 'l', 'l', 'o']
list操作:
head:返回第一个元素,[ ]报错
tail:返回去除第一个元素的list,[ ]报错
++:连接两个list
!! :获取指定索引(start 0)的元素,所有的中缀运算符都可以加上()变成前缀运算符, 中缀运算符指定任意一个参数后都会返回一个part application函数。
[1, 2, 3] !! 0 = (!!) [1, 2, 3] 0 = 1
( (!!) "dog" ) 2 = ("dog" !! ) 2 = (!! 2) "dog" = 'g'
length:length "dog" = 3
reverse:reverse "dog" = "god"
elem:elem 'g' "dog" = True 检查指定元素是否在list中
take:返回前n个元素组成的list
drop:返回去除前n个元素之后的list
zip:把两个list合成tuple构成的list
zip [1,2,3] [2,4,6] = [(1,2),(2,4),(3,6)]
cycle:循环一个list的元素
cycle [1, 2, 3] = [1, 2, 3, 1, 2, 3, 1 .. ]
8,递归
haskell没有for while之类的依靠状态的循环,所有循环问题都使用递归
The way to solve recursive functions is by following this simple set of rules:
1 Identify the end goal(s).
2 Determine what happens when a goal is reached.
3 List all alternate possibilities.
4 Determine your “rinse and repeat” process.
5 Ensure that each alternative moves you toward your goal.
先识别递归结束条件,然后确定每个结束条件满足时如何退出递归,列出到达结束条件之前的中间状态,确定循环,确定每次循环都在向结束条件靠近。
求最大公约数:
myGCD a b = if remainder == 0
then b
else myGCD b remainder
where remainder = a `mod` b
实现length
myLength [] = 0
myLength xs = 1 + myLength (tail xs)
实现take
myTake _ [] = []
myTake 0 _ = []
myTake n (x : xs) = x : rest
where rest = myTake (n - 1) xs
实现cycle:环状队列
myCycle (first : rest) = first : myCycle (rest ++ [ first ])
9,高阶函数
A higher-order function is technically any function that takes another function as an argument. Typically, when higher-order functions are mentioned, a specific group of them comes to mind, and nearly all of these are used to abstract away common patterns of recursion.
map:The map function takes another function and a list as arguments and applies that function to each element in the list。
map (\x -> x + 1) [1, 2, 3] = [2, 3, 4]
map ("a "++) ["train", "plane", "boat"] = ["a train", "a plane", "a boat"]
map (^2) [1,2,3] = [1,4,9]
递归实现map:
myMap f [] = []
myMap f (x:xs) = (f x) : myMap f xs
filter:The filter function looks and behaves similarly to map, taking a function and a list as arguments and returning a list. The difference is that the function passed to filter must be passed a function that returns True or False. The filter function works by keeping only the elements of the list that pass the test。
filter (\(x:xs) -> x == 'a') ["apple","banana"] = ["apple"]
递归实现filter:
myFilter test [] = []
myFilter test (x:xs) = if test x
then x : myFilter test xs
else myFilter test xs
fold:The function foldl (the l stands for left) takes a list and reduces it to a single value. The function takes three arguments: a binary function, an initial value, and a list.
递归实现foldl
myFoldl f init [] = init
myFoldl f init (x:xs) = myFoldl f newInit xs
where newInit = f init x
递归实现foldr
myFoldr f init [] = init
myFoldr f init (x:xs) = f x rightResult
where rightResult = myFoldr f init xs
eg:
-- 左折叠初始值在前,右折叠初始值在右
foldl (-) 0 [1,2,3,4] = -10
foldr (-) 0 [1,2,3,4] = -2
10, 模拟oop
利用闭包保存属性:定义一个机器人,属性包括名字,攻击力,生命值
-- 构造方法
robot (name, attack, hp) = \message -> message (name, attack, hp)
通过向向对象发送消息执行行为
-- getter
getName aRobot = aRobat (\(n, _, _) -> n)
getAttack aRobat = aRobat (\(_, a, _) -> a)
getHp aRobat = aRobat (\(_, _, h) -> h)
-- setter方法每次返回新对象
setName aRobot name = aRobat(\(n, a, h) -> robat(name, a, h))
printRobot aRobot = aRobot (\(n,a,h) -> n ++
" attack:" ++ (show a) ++
" hp:"++ (show h))
-- aRobot收到attackDamage这么多的攻击
damage aRobot attackDamage = aRobot (\(n, a, h) ->
robot (n, a, h - attackDamage))
-- aRobot攻击defender一下,根据攻击力造成伤害
fight aRobot defender = damage defender attack
where attack = if getHP aRobot > 10
then getAttack aRobot
else 0
-- test
killerRobot = robot ("Kill3r",25,200)
gentleGiant = robot ("Mr. Friendly", 10, 300)
-- 战斗三个回合
gentleGiantRound1 = fight killerRobot gentleGiant
killerRobotRound1 = fight gentleGiant killerRobot
gentleGiantRound2 = fight killerRobotRound1 gentleGiantRound1
killerRobotRound2 = fight gentleGiantRound1 killerRobotRound1
gentleGiantRound3 = fight killerRobotRound2 gentleGiantRound2
killerRobotRound3 = fight gentleGiantRound2 killerRobotRound2
利用oop模拟函数式就很简单了:参考java8中lambda的实现。