用纯函数式思维在Java8下写的一段奇葩程序

首先说一下什么是纯函数式。在我的理解,“纯函数式”用一句话就可以描述:Anything is value.——我的理解不一定准确,但我就是这么理解的。

就是所有的东西都是值——没有变量;包括函数在内都是值——是值,就可以传递(包括函数)。

为什么说这段程序是奇葩呢?

其一、传统的Java是面向对象的,自从Java8中加入了lambda,Java就变成了“面向对象”和“函数式”两种方式的混合语言。这段程序全部使用lambda的语法来写,与平常写的Java风格完全不同。

其二、在Java的对象中保存数据通常是用对象的属性,lambda表达式本质上仍然是对象,但它并没有属性,但我们却成功的在lambda中保存了数据,这相对于传统的Java编程思维也是一种跳跃。

其三、在Scheme中实现同样的函数(或lambda)非常简洁,也很容易读,而在Java中的实现,可读性好差,以致于我自己都快看不懂了,所以说是“奇葩”。

这段程序用两种方式实现了同样的功能:

1.实现一个函数cons,这个函数有两个参数x和y,并返回一个东西(这个东西以下简称为c)。

2.实现一个函数car,传入c,并返回原来传入cons中的x。

3.实现一个函数cdr,传入c,并返回原来传入cons中的y。

这实际上是Scheme中自带的“序偶”,不过即使Scheme语言本身的库不自带cons,我们自己实现也是很简单的(下面的程序中,我在注释部分列出了Scheme的实现);Java骨子里是面向对象的基因,如果用面向对象的方式来实现上述功能是非常简单的,但用lambda的语法来实现就显得奇葩了。

下面先把奇葩贴出来,然后在后面的注释中解释一下:



import java.util.function.BiFunction;
import java.util.function.Function;

public class TestCons {

public static void main(String[] args) {
        testCons1();
        testCons2();
    }

private static void testCons1() {
        /*
        (define (cons x y)
          (lambda (m)
            (cond ((= m 0) x)
                  (else y))))
        (define (car z) (z 0))
        (define (cdr z) (z 1))
        上面几行Scheme代码翻译成Java是如下三行代码
         */
        BiFunction<Object, Object, Function<Integer, Object>> cons = (x, y) -> m -> m == 0 ? x : y; // 注释1
        Function<Function, Object> car = z -> z.apply(0); // 注释2
        Function<Function, Object> cdr = z -> z.apply(1); // 注释3

Function c = cons.apply(3, "abc"); // 调用cons,并传入两个值,创建了对象c
        System.out.println(car.apply(c)); // 从c中取出第一个值
        System.out.println(cdr.apply(c)); // 从c中取出第二个值
    }

private static void testCons2() {
        /*
        (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)))
        上面几行Scheme代码翻译成Java是如下三行代码
         */
        BiFunction<Object, Object, Function<BiFunction, Object>> cons = (x, y) -> f -> f.apply(x, y); // 注释4
        Function<Function<BiFunction, Object>, Object> car = f -> f.apply((a, b) -> a); // 注释5
        Function<Function<BiFunction, Object>, Object> cdr = f -> f.apply((a, b) -> b); // 注释6

Function c = cons.apply(3, "abc"); // 调用cons,并传入两个值,创建了对象c
        System.out.println(car.apply(c)); // 从c中取出第一个值
        System.out.println(cdr.apply(c)); // 从c中取出第二个值
    }
}



注释1:此行创建一个叫cons的lambda表达式,此表达式有两个参数x和y,并返回另外一个lambda,这个lambda有一个整数类型的参数m,且当m为0时,返回x,否则返回y。

注释2:此行创建一个叫car的lambda,此lambda有一个参数,且这个参数也是一个lambda(z),car的lambda体中是把0传入z中,并得到返回值。

结合“注释1”和“注释2”这两行,我们可以这样解释:cons返回的lambda可以做为car的参数。

注释3:和“注释2”差不多,不再缀述。

注释4:此行创建一个叫cons的lambda,此lambda有两个参数x和y,反返回另外一个lambda,这个lambda有一个参数,且这个参数也是一个lambda(f),在cons返回值的lambda体中应用f,并把cons的两个参数做为f的两个参数——相当拗口——简单点说就是cons并不做什么,只是把x和y,交给一个lambda,而这个lambda也不做什么,只是等着另外一个lambda(f)来处理x和y,而这个f要通过参数传过来。

注释5:此行创建一个小car的lambda,此lambda有一个参数(此参数可传入cons返回的lambda),从“注释4”中我们知道cons返回的lambda还需要一个lambda做为参数来处理两个参数,所以我们传入一个(a, b) –> a,这里在a和b中返回前者,这就是car的目的。

注释6:和“注释5”差不多,不再缀述。

到处都是lambda,很难读,但在Scheme中完全一样的算法实现就很简洁,可读性很好,这是为什么呢?

我认为这是S表达式的语法结构形成的效果——S表达式是以数据结构的方式存储程序的,这样的情况下,假设Scheme中没有lambda,此时我们要扩展编译器来支持lambda,则我们不需要修改编译器的parser部分——但Java的lambda没有办法与现有的其它语法的结构一样,所以就只能新增新的语法结构了,但又要与原有的基因融合,这样虽然lambda在本质上仍然是对象,但在表现形式上与原有的Java却有很大的排异反应。

这不是一两句话能说得明白的,也有点扯远了。

下面再演示一个邱奇计数的例子,这个就不写注释了:



import java.util.function.Function;

public class testChurchNum {
    public static void main(String[] args) {
        Function<Function<Function<Object, Object>, Function>, Function<Function<Object, Object>, Function>>
                add_1 = n -> f -> x -> f.apply(n.apply(f).apply(x));

Function<Function<Object, Object>, Function> zero = f -> x -> x;
        Function<Function<Object, Object>, Function> one = add_1.apply(zero);
        Function<Function<Object, Object>, Function> tow = add_1.apply(one);
        Function<Function<Object, Object>, Function> one_1 = f -> x -> f.apply(x);
        Function<Function<Object, Object>, Function> tow_1 = f -> x -> f.apply(f.apply(x));

Function f = x -> (((Integer) x) + 1);
        System.out.println(zero.apply(f).apply(0));
        System.out.println(one.apply(f).apply(0));
        System.out.println(one_1.apply(f).apply(0));
        System.out.println(tow.apply(f).apply(0));
        System.out.println(tow_1.apply(f).apply(0));
    }
}



用纯函数式思维在Java8下写的一段奇葩程序

时间: 2024-10-15 19:42:56

用纯函数式思维在Java8下写的一段奇葩程序的相关文章

怎么去写好一段优雅的程序

此文已由作者吴维伟授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 写好一段优雅程序的必要条件是良好的设计. 写程序就像在走一个迷宫.编写之初,有若干个可能的解决方案萦绕在我们的脑海.我们选择一个继续深入,可能达到终点--实现了功能需求,但更大的可能是进入了一个死胡同或者一个新的岔路口,需要重新进行抉择,如此反复. 想起一年前的自己,仅凭着生物的本能去写着代码:我依照着以往的经验,先写了一段.然后刷新一下页面,查看是否离实现需求更近了一步.幻想着程序可以完美运行的我看到最

第一部分 学习函数式思维

第一部分学习函数式思维 捡起这本书的原因可能很多.可能是在学习 LINQ 和 C# 3.0,或者受此影响的别的技术时,听到过函数式编程,你想知道它是否有还其他意思:也可能听说过函数式编程可以使写并行或异步程序更容易:也许听说过函数风格的其他有趣应用,比如,如何写出没有可变状态的程序:还可能已听说过一种叫 F# 的新语言,将会成为 Visual Studio 2010 的一部分,你可能想知道它到底提供了什么. 不管哪种情况,学习函数式编程的第一件事,就是要建立不同于以往的基本概念.但这并不是说就得

Frege-基于JVM的类Haskell纯函数式编程语言

Frege是一门受Haskell语言启示而设计的纯函数式编程语言.Frege程序会被编译为Java,并执行于JVM上.它与Haskell是如此的类似.以至于有人称它为JVM上的Haskell.取Frege这个名字是为了纪念德国数学家.逻辑学家.哲学家Gottlob Frege. 语言的主要特色 纯函数式编程语言 继承了函数式编程语言Haskell的精神.默认就具有不变性(immutability). 执行于JVM之上 Frege程序编译为Java bytecode.执行于JVM(Java 7+)

纯CSS实现的二级下拉菜单效果代码实例

纯CSS实现的二级下拉菜单效果代码实例:二级下拉是最为常用的效果之一,当前的此效果一般哟结合js实现,本章节介绍一个使用纯CSS实现的二级下拉菜单效果,希望能够给需要的朋友带来一定的帮助,不过此代码也有一点浏览器兼容问题,那就是在IE6中不兼容.代码如下: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="author" conten

函数式思维的引入

这是译文,原文:http://fsharpforfunandprofit.com/posts/thinking-functionally-intro/ 改变你思考的方式 函数式思维不仅仅是形式上的区别,它是完全不同于完全面向对象的编程思维(和传统命令式语言,比如C,也不同),这一点很重要. F#允许非函数式思维,它尝试保留你已经熟悉了的习惯.你可能不改变你的思维方式--使用F#以非函数的思维,而没有意识到你失去了很多.为了得到更多F#的东西,为了流畅地和舒服地采用函数式编程,以函数式思维而不是命

纯CSS3实现淡入淡出下拉菜单

纯CSS3实现淡入淡出下拉菜单是一款比较简单清新的CSS3教程下拉菜单,这款下拉菜单是垂直方向的,点击主菜单项可以展开和折叠子菜单,在展开折叠的过程中伴随着淡入淡出的动画效果 源代码:http://www.huiyi8.com/css3/ 纯CSS3实现淡入淡出下拉菜单,布布扣,bubuko.com

c++中向任意目录下写文件

今天遇到了在C盘的子目录下写文件出错的事情,最中自己实验后解决.小小记录一下. 代码如下: #include<stdio.h>#include<stdlib.h>#include<time.h> main(){ int data[35]; int n=35,temp; for(int i=0;i<35;i++) {  data[i]=i; } srand((unsigned)time(NULL)); FILE *fp; fp=fopen("C:\\Pro

使用Code::blocks在windows下写网络程序

使用Code::blocks在windows下写网络程序 作者 He YiJun – storysnail<at>gmail.com 团队 ls 版权 转载请保留本声明! 本文档包含的原创代码根据General Public License,v3 发布 GPLv3 许可证的副本可以在这里获得:http://www.gnu.org/licenses/gpl.html 本文档根据GNU Free Documentation License 1.3发布 GFDL1.3许可证的副本可以在这里获得:ht

纯CSS实现的二级下拉导航菜单实例代码

纯CSS实现的二级下拉导航菜单实例代码:二级下拉菜单在众多的网站都有应用,不但能够有效的组织分类导航,并且能够节省大量的网站空间,也能够实现网站的动态化效果,大多数二级导航菜单都是结合javascript实现,本章节介绍一下使用纯css实现的下拉菜单.代码如下: <!DOCTYPE html> <html> <head> <meta charset=" utf-8"> <meta name="author" co