最近看了一个视频,是ScottWlaschin在伦敦进行一个关于函数式编程模式的演讲 这个演讲给了我很多启发,所以顺便结合自己的理解,做一下终结。
这里有几句关于几种编程范式的话,觉得挺有意思的:
Do I need to preserve state? (e.g: some entity in a game) -> OOP.
Am I transforming some input into some kind of output? -> functional. (most of the code).
Something algorithmicky? -> procedural.
再配上ppt上面的图,好搞笑,哈哈
Functions 函数
Types 类型
Composition 组合
这里其实说的是对函数式语言的一些直观的理解,可以从这几个角度去理解,什么 是函数式范式与面向对象相比,为了同样的工程目标,用了完全不同的概念。
函数式语言里面的确很多数学的概念,映射,尾递归,变换,不可变变量,高阶 函数等用数学的角度去处理数据以前在学习高等代数的时候,一个很重要的概念 就是映射,矩阵就是一个映射𝒜。而映射可以作用于不同的集合。
- Input and output values already exit
- A function is not a calculation , just a mapping
- Input and output values are unchanged(immutable不可变的变量)
从上面这几点可以看出,函数式语言里面的函数的概念其实可以理解为一个映射 𝒜/f(x)的意思而不是运算的集合。
比较有趣的是这一个,Functions can work on functions 元素是函数的集合之间 也是可以映射的,也就是输入函数,然后输出是函数,这里可以明白为什么函数式 语言函数是可以作为参数,返回值了。
- Purity 什么是纯函数?
Pure functions are easy to reason about
customer.SetName(newName); //setName之后customer状态是不是被改变了?
var name = customer.GetName();
let newCustomer = setCustomerName(aCustomer,newName)
let name,newCustomer = getCustomerName(aCustomer)
//在函数式语言里面,没有状态所以更加pure?
Pure functions are easy to refactor
let x = doSomething()
let y = doSomethingElse(X)
return y + 1
- 另外一些如果函数是pure的好处
- Laziness 延迟求值 only evaluate when I need the output
- Cacheable results same answer every time – “memoization” 缓存函数结果
- No order dependencies I can evaluate them in any order I like 执行的时候,不需要根据函数定义的顺序,不用跟c语言那样
- Parallelizable “embarrassingly parallel” 可以拥抱并发
上面说的各种也就是函数没有状态没有副作用,而且变量不可变 ==> 就代表了 purity 这里我想起erlang里面,其实很多时候函数都不是纯函数,其实也是有side effects的
函数式语言里面的类型跟类还是有很大区别,虽然类本质上也是一堆数据(函数也可以看成是数据)
什么是类型? 类型应该是数据的定义 类型区分了数据Data以及数据操作的行为 Behaviour
let z = 1; let add x y = x + y; 变量的定义跟函数的定义都是用
let
关键字
数学里面其实可以认为一切都是函数,f(x) = C,其实我们通常说的常量,在数学概念的理解,应该叫 常函数。
函数可以做参数,可以做返回值,可以在函数内定义,就是说一般 变量能出现的地方,函数也能出现。
函数式语言没有继承,接口的概念,基本都是利用组合去实现继承的功能。
Types represent constraints on input and output
类型代表输入输出的约束,也是程序的文档
type Suit = Club | Diamond | Spade | Heart
type String50 = // non-null, not more than 50 chars
type EmailAddress = // non-null, must contain ‘@’
type StringTransformer = string -> string
type GetCustomer = CustomerId -> Customer option
type PaymentMethod =
| Cash
| Cheque of ChequeNumber
| Card of CardType * CardNumber
OO version:
interface IPaymentMethod {..}
class Cash : IPaymentMethod {..}
class Cheque : IPaymentMethod {..}
class Card : IPaymentMethod {..}
class Evil : IPaymentMethod {..}
函数就是一个输入集合,输出集合之间的变换 从图左上角看到,外部数据与系统内部的数据类型是怎么转换的 可以更加清晰的理解系统的逻辑,系统逻辑是由数据驱动的,系统的逻辑就是数据流的表现
参数化所有的东西
public static int Product(int n) {
int product = 1;//初始化
for (int i = 1; i <= n; i++)//遍历
{
product *= i;
}
return product;
}
public static int Sum(int n)
{
int sum = 0;//初始化
for (int i = 1; i <= n; i++)//遍历
{
sum += i;
}
return sum;
}
Sum 与 Product 里面 for 这个遍历操作是一样的,可以抽象出来
如果用F#是这么写
let product n =
let initialValue = 1
let action productSoFar x = productSoFar * x
[1..n] |> List.fold action initialValue
let sum n =
let initialValue = 0
let action sumSoFar x = sumSoFar+x
[1..n] |> List.fold action initialValue
let printList anAction aList = for i in aList do
anAction i
// val printList :
// ('a -> unit) -> seq<'a> -> unit
Object-oriented strategy pattern:
class MyClass
{
public MyClass(IBunchOfStuff strategy) {..}
int DoSomethingWithStuff(int x)
{
return _strategy.DoSomething(x) }
}
F#:
let DoSomethingWithStuff strategy x = strategy x
Functional equivalent of decorator pattern
let add1 x = x + 1 // int -> int
let logged f x =
printfn "input is %A" x
let result = f x
printfn "output is %A" result
result
let add1Decorated = // int -> int
logged add1
[1..5] |> List.map add1
[1..5] |> List.map add1Decorated