函数式编程Functional Programming


FP是一种将计算是为对表达式求值的编程范式

FP优点

  • 可组合性和模块化
  • 表达性
  • 可靠性和测试
  • 易于并发
  • 延迟求值
  • 生产力
  • 正确性
  • 可维护性

FP原则

  • 高阶函数(HOF)作为头等值
  • 不可变性
  • 纯函数
  • 声明式编程风格

HOF


FP中函数作为头等值,意味着函数可以由变量命名、分配给变量,可以出现在任何构造可以出现的地方。

HOF使编程专注于结果,而不是步骤。

HOF优点

  • 组合和模块化
    • 实现函数式组合的方式
      • 组合
      • 柯里化
      • 部分应用函数
  • 代码可重用性
  • 创建高度动态和适应性强的系统

HOF与lambda

一种重构代码,减少代码冗余的方式,一个优雅的解决方案。

  • 简洁的内联编码
  • 限制类级别变量的暴露
  • 代码流易读性高

Example:

  • stream在使用后被Disposed,一种不可重复使用的模式。
string text;
using(var stream = new StreamReader(path))
{
  text = stream.ReadToEnd();
}
  • 灵活、可重用的清理一次性资源的方式。
public static R Using<T,R>(this T item,Func<T,R> func) where T:IDisposable
{
  using(item)
  {
    return func(R);
  }
}

string text = new new StreamReader(path).Using(s => s.ReadToEnd);

Currying

  • 每个函数只具有一个参数
    • 专用函数更易于重用
  • 多个参数的函数将转换成函数序列的求值,即生成函数链

C#中的Curry & UnCurry

pubilc static Func<T,Func<R,S>> Curry<T,R,S>(this Func<T,R,S> func)
{
  return t => r => func(t,r);
}

pubilc static Func<T,R,S> UnCurry<T,R,S>(this Func<T,Func<R,S>> func)
{
  return (x,y) => func(x)(y);
}

部分应用函数

将多个参数固定到一个函数并生成参数更少的函数

public static Func<T,S> Partial<T,R,S>(this Func<T,R,S> func,T arg)
{
  return arg2 => func(arg,arg2);
} 

Currying & 部分应用函数

Example:

一个通用的Retry方法里,

public static T Retry<T>(Func<T> func){...}

无法适用于带参数的情况,重写一个新版本的带有参数的的Retry函数并不是一个理想的选择,因为每个方法的参数个数可能不一致,为每种情况重写一个方法十分不优雅。

更好的选择是使用部分应用函数和Curry方法:

public static Func<R> Partial<T,R>(this Func<T,R> func,T arg)
{
  return () => func(arg);
}
public static Func<T,Func<R>> Currying<T,R>(this Func<T,R> func)
{
  return arg => func(arg);
}

Func<string,string> readText = (path) => ReadText(path);
string text = readText.Partial(path).Retry();

Func<string,Func<string>> curryRead = readText.Curry();
string text = curryRead(path).Retry();