My Blogs

Posts tagged with "函数式编程"

编程范式

Tags: 函数式编程 , 面型对象编程 , 范式

Published 2021年12月10日 12:10 by james

TL;DR

一句话总结

如你所见,我在介绍三个编程范式的时候,有意采用了上面这种格式,目的是凸显每个编程范式的实际含义——它们都从某一方面限制和规范了程序员的能力。没有一个范式是增加新能力的。也就是说,每个编程范式的目的都是设置限制。这些范式主要是为了告诉我们不能做什么,而不是可以做什么。

另外,我们应该认识到,这三个编程范式分别限制了goto语句函数指针赋值语句的使用。那么除此之外,还有什么可以去除的吗? 没有了。因此这三个编程范式可能是仅有的三个了——如果单论去除能力的编程范式的话。支撑这一结论的另外一个证据是,三个编程范式都是在1958年到1968年这10年间被提出来的,后续再也没有新的编程范式出现过。

编程范式与软件架构的关系

大家可能会问,这些编程范式的历史知识与软件架构有关系吗? 当然有,而且关系相当密切。譬如说:

这和软件架构的三大关注重点不谋而合: 功能性、组件独立性以及数据管理。

函数式编程模式

Tags: 面型对象编程 , 函数式编程 , 模式

Published 2021年11月08日 11:11 by james

模式词汇表1

替代面向对象模式

这一部分会向你展示如何采用函数式语言的特性来替代普通的面向对象模式。这样做通常可以减少我们需要编写的代码数量,从而让我们维护的代码变得更加简洁。

模式1 替代函数式接口

在这一模式中,我们采用原生的函数式特性来替代像RunnableComparator这样常见的函数式接口。

在这一部分中,我们引入了两个基本的函数式特性。 - 第一个特性是高阶函数(higher-order function),它允许我们将函数作为头等数据进行传递。 - 第二个特性是匿名函数,它允许我们编写快捷的一次性函数,而无需为其指定函数名。

通过将这两个特性相结合,我们可以用一种非常简洁的方式来替代大多数的函数式接口实例。

模式2 替代承载状态的函数式接口

我们采用这种模式来替代那些需要承载某些状态信息的函数式接口实例,为此我们引入了一个新的特性: 闭包。闭包可以将某个函数和某些状态进行打包传递

模式3 替代命令模式

替代命令模式将行为封装于某个对象之中。

在这一模式中,我们将了解如何使用在前两种模式中所介绍的技术来替代面向对象版本的命令模式。 …

零基础构建语言解释器

Tags: 函数式编程 , 程序设计语言理论 , Lambda , Lisp , JavaScript , 编译器 , 解释器

Published 2015年04月01日 23:00 by james

在编写Interpreter之前,我们需要先了解Lexer(词法分析器),Parser(语法解析器),AST(抽象语法树)。

一般情况下,Interpreter在解释执行程序时,一般会经过如下步骤。

  1. Lexer读入程序代码,把代码转换token序列。
  2. Parser把读到的token序列转换为AST(大部分情况下,Lexer内嵌为Parser的一部分)。
  3. 对AST进行Lowering(化简AST)或者desugar(把语法糖的AST节点转换为标准等价AST节点)处理。
  4. Interpreter递归执行AST,AST决定了代码的执行顺序。

alternative text

介绍完了基本的一些概念,我们现在开始来实现语言解释器。

首先,我们需要先定制文法规则,一般情况下,我们只要制定好了文法规则,就可以找一些工具来帮我们生成AST,对于复杂的文法规则,手写Parser是非常麻烦并且乏味的。 这里,为了简单,我们选用S-Expression来作为我们的文法规则来实现Lambda Calculus(Lambda演算)的解释器。 通过该Interpreter的实现,大家可以很容易就搞明白程序设计语言中耳熟能详却又未必深刻了解的一些概念,比如:类型,Lexical Scope(词法作用域),Dynamic Scope(动态作用域),闭包等概念。

Lambda Calculus

虽然规则简单,Lambda演算却是一门强大的语言。它的三个元素分别是是:变量,函数,调用。用传统的表达法,它们看起来就是:
    变量:x
    函数:λx.t
    调用:t1 t2

每个程序语言里面都有这三个元素,只不过具体的语法不同,所以你其实每天都在使用 lambda calculus。换成S-Expression就是:
    变量:x
    函数:(lambda (x) e)
    调用:(e1 …

Y-Combinator不同语言实现方案

Tags: 函数式编程 , 程序设计语言理论 , Lambda , 数学 , JavaScript

Published 2014年09月19日 19:00 by james

递归和定点

纯λ演算的一大特色是可以通过使用一种自应用技巧来书写递归函数。

f(n) = if n = 0 then 1 else n*f(n-1) 
f = λn.if n = 0 then 1 else n*f(n-1)

把f移到等式的后面,得到函数G

G = λf.λn.if n = 0 then …

Lambda演算之Y-Combinator的推导(JS描述)

Tags: 函数式编程 , 程序设计语言理论 , Lambda , 数学 , JavaScript

Published 2014年09月18日 19:00 by james

上一节中,我们讲到了如何使用λ演算来描述自然数,可以看出λ演算的表现力确实非常强大,然而遗憾的是,由于lambda演算中使用的都是匿名函数,所以它无法很直观地表述递归。 如果缺少了递归,λ演算的能力无疑会大打折扣。

所有基于λ演算的语言中,其实都是支持对过程进行命名的,那为什么这里我们还需要探讨匿名函数的递归呢?

  1. 为了保持纯洁性,我们希望仅仅通过λ演算的规则本身来解决递归的问题。
  2. 部分语言对过程的命名必须等到该过程定义完成后才可以进行,这个时候我们必须找到一种方式使其能够很容易地获得递归的能力。

下面我们就是用factorial函数为原型,来看看如何使用lambda演算基本的规则,为其增加递归的能力。

传统定义方式,不过这个方式不符合我们的需求,因为它使用到了函数自身fact。

var fact = function(n){
    return n <= 1 ? 1 : n * fact(n - 1);
}; …