博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
再谈函数和一等公民
阅读量:6134 次
发布时间:2019-06-21

本文共 3440 字,大约阅读时间需要 11 分钟。

本文首发自本人博客 以下是原文:

写篇文章再谈谈函数和一等公民,因为我发现了些有趣的东西。

先前想在自己的 里实现 元组 这种数据结构,但是没有什么方向,就去看了下 Scheme 的语法,看了下 Wiki,然后不知不觉间,看到了用 Lisp 实现 Pair。

Lisp 你可能听过,这里我们不需要它,但后面的 Pair 是啥,它其实是一种很简单的数据结构,类似这样,用 TyepScript 表示就是这样:

// 创建一个 pairfunction cons(l: number, r: number) {    return [l, r]}// 取出 pair 的左边 function car(my_pair: number[]) {    return my_pair[0]; }// 取出右边 function cdr(my_pair: number[]) {    return my_pair[1]; }复制代码

闲话一下,scheme 里创建 pair 的函数名就是 cons,还有它的两个操作 car 和 cdr 也是这个名字,因此本文也都用这个名字。

好吧,进入正题,形如上面这种样子的数据结构,叫做 Pair,在很多 js 库里也有它的存在,比如 React.useState 返回的是左边是值,右边是 dispatcher 的 pair。

但我们这里讨论的是利用了数组去实现的,有没有别的方法去实现这种数据结构呢?答案当然是有的啦,下文将会给出仅利用函数的方式来实现这种数据结构,以及仅用函数去实现链表、二叉树。

Pair 的函数式实现

因为本人一开始是想在自己写的的一个函数式方言的解释器里加上 pair 这个类型的,因此看了 Pair 的函数式实现,如下:

;; 代码从 Wiki 摘录;; https://en.wikipedia.org/wiki/Cons(define (cons x y)    (lambda (m) (m x y)))(define (car z)    (z (lambda (p q) p)))(define (cdr z)    (z (lambda (p q) q)))复制代码

翻译成 TypeScript 就是这样:

// 姑且把 Pair 这种数据结构类型定义为函数type Pair = Function; // 创建一个 consfunction cons(x: number, y: number): Pair {    return (how_to_do_it: Function) => {        return how_to_do_it(x, y);     }}// 取出 pair 的左值function car(pair: Pair) {    return pair((l, r) => l)}// 取出 pair 的右值function cdr(pair: Pair) {    return pair((l, r) => r)}const xy = cons(1, 2); // 调用 cons 返回一个函数const x = car(xy); // => 1 const y = cdr(xy); // => 2复制代码

以上代码完美的体现了函数是一等公民这个概念,因为我们仅利用了函数去实现数据结构,这才是一等公民背后的意义。

挑战:函数式链表

现在,我们有了 Pair,它有两个值,此外,这两个值也是有序的,它可以用来构造链表吗?

当然可以,因为我们没有考虑到它有两个值里的值是啥。Pair 里的左值右值,既可以是数字,也可以是另一个 Pair,也就是说,Pair 里可以嵌套 Pair,据此可以递归地定义链表:

Pair(number, number | Pair | null) 当且仅当右值为 null 的时候,我们才认为链表到达了尽头

由上面的讨论可以很容易的利用 Pair 递归地构造出链表,也就是说,可以仅用函数实现链表。

或许你还没理解所谓递归地定义链表是啥意思,你可以看看下面的代码形式,便一目了然:

cons(1, cons(2, cons(3))) 1 -> 2 -> 3

根据上面的讨论,其 TypeScript 实现如下:

type Pair = Function;function cons(x: number, y?: number | Pair): Pair {    return (how_to_do_it: Function) => {        return how_to_do_it(x, y);     }}function car(pair: Pair) {    return pair((l, r) => l)}function cdr(pair: Pair) {    return pair((l, r) => r)}// 取出链表的第 n 个function nth(pair: Pair, n: number) {    if (n === 0) {        return car(pair);     } else {        return nth(cdr(pair), n - 1);     }}const lst = cons(1, cons(2, cons(3)))// 1 -> 2 -> 3 const number = nth(lst, 0); // => 1 复制代码

可以看到,cons 的 y 可以为空或者也是一个 pair 了,这里存在递归,因此取链表的时候用递归实现比较容易。

挑战:函数式二叉树

上面的讨论已经实现了链表,而链表里一个最特殊的地方便是引用,如果引用变成两个,链表就可以推广成二叉树。

以下是我的实现:

// 在把函数作为一等公民的语言里,// 函数是一种数据类型/结构type Tree = Function;// 函数式二叉树// 如果不指定范型,则认为树上存的值是数字; function Tree
(v: T, l?: Tree, r?: Tree): Tree { return (how_to_do_it: Function) => { return how_to_do_it(v, l, r); }}// 取左子树function lchild(tree: Tree) { return tree((v, l, r) => l);}// 取右子树function rchild(tree: Tree) { return tree((v, l, r) => r); }// 取值 function valOf(tree: Tree) { return tree((v, l, r) => v); }// 函数式二叉树的前序遍历function traversal(t?: Tree) { if (!t) return; const l = lchild(t); const r = rchild(t); const v = valOf(t); console.log(v); traversal(l); traversal(r); }// 创建一棵树。。。// 虽然有点绕... const t = Tree(1, Tree(2, Tree(3), Tree(4)), Tree(5, Tree(6)))// 结构如下: // 1// / \// 2 5 // / \ /// 3 4 6 // 先序遍历结果traversal(t); // => 打印:123456复制代码

上面的代码实现了二叉树的表示,并给出了二叉树的先序遍历方式。

那么,什么是函数呢?

我还不知道 233,不过时断时续的学习函数式语言,每次都有不同的收获是真。

上面的说明里可以窥见,所谓函数是一等公民,其实意味着函数可以实现编程语言里的其他事物,如数据结构。

这部分涉及到了 Lambda Calculus 这方面我学习的不深。。。。不展开了。

转载地址:http://hteua.baihongyu.com/

你可能感兴趣的文章
bash complete -C command
查看>>
解决zabbix 3.0中1151端口不能运行问题
查看>>
售前工程师的成长---一个老员工的经验之谈
查看>>
Get到的优秀博客网址
查看>>
dubbo
查看>>
【Git入门之四】操作项目
查看>>
老男孩教育每日一题-第107天-简述你对***的理解,常见的有哪几种?
查看>>
Python学习--time
查看>>
在OSCHINA上的第一篇博文,以后好好学习吧
查看>>
高利率时代的结局,任重道远,前途叵测
查看>>
phpcms v9栏目列表调用每一篇文章内容方法
查看>>
python 自定义信号处理器
查看>>
luov之SMTP报错详解
查看>>
软件概要设计做什么,怎么做
查看>>
dwr
查看>>
java的特殊符号
查看>>
word2010中去掉红色波浪线的方法
查看>>
fabric上下文管理器(context mangers)
查看>>
JQuery-EasyUI Datagrid数据行鼠标悬停/离开事件(onMouseOver/onMouseOut)
查看>>
并发和并行的区别
查看>>