haskell基础

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中的函数跟数学中的函数一致,所有函数名都以小写字母开头

function.png

函数的三个特征:
 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
let.png

变量在GHCi里是可以重新赋值的,只是为了方便!!!

4,lambda

lambda.png

lambda可以替换where/let中的变量定义

calcChange owed given = (\change -> 
            if change > 0
                then change
                else 0) (given - owed)
变量范围.png

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.png

递归实现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的实现。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi阅读 7,279评论 0 10
  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 13,727评论 0 38
  • 1、InnoDB行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据,InnoDB才使用行级锁,否则,...
    Do_40阅读 329评论 0 1
  • 做什么大于怎么做 坏的产品坏的千奇百怪,好的产品都有相似的原因。就和做人一样,做产品经理需要遵循原则。设计的原则,...
    硅谷堂阅读 1,112评论 5 11