您的位置:1010cc时时彩经典版 > 1010cc时时彩经典版 > 而不是怎么做,关于Java8函数式编程你需要了解的

而不是怎么做,关于Java8函数式编程你需要了解的

发布时间:2019-08-17 23:27编辑:1010cc时时彩经典版浏览(160)

    关于Java8函数式编程你需要了解的几点,Java8函数式编程

    函数式编程与面向对象的设计方法在思路和手段上都各有千秋,在这里,我将简要介绍一下函数式编程与面向对象相比的一些特点和差异。

    编程范式

    编程范式

    什么是函数式编程思维? - 面向对象编程 - 知乎
    https://www.zhihu.com/question/28292740

    C#函数式编程,

    编程语言范式

    常见的编程范式有命令式编程(Imperative programming)函数式编程逻辑式编程;

    许多现存的编程语言都可基于其计算模型加以分类,归入某些语言族,或者属于某种编程范式。按照不同的规则,可以有多种分类的方法,而且不同的学者对某些语言的具体归属也有不同的意见。

    给出一种系谱:

    说明式(Declarative )                   命令式( Imperative

      函数式  Lisp, ML, Haskell                冯诺依曼 C, Ada, Fortran

      数据流  ld, Val                         脚本式 Perl, Python, PHP

      逻辑式  Prolog                         面向对象 Smalltalk, C , Java, C#

      基于模板 XSLT

    有些编程范式并不能按以上的方法进行分类,比如:元编程,泛型编程。

    一种语言并不是只从属于一种编程范式,有些语言本身就是为支持多范式设计的;

    比如:Lisp就同时支持函数式编程、面向对象、元编程。

    命令式编程是面向计算机硬件的抽象,有变量(对应着存储单元),赋值语句(获取,存储指令)表达式(内存引用和算术运算)和控制语句(跳转指令);

     

    函数式编程

    定义

    In computer science, functional programming is a programming paradigm that treats computation as the evaluation of mathematical functions and avoids state and mutable data.

    函数式编程是一种编程模型,他将计算机运算看做是数学中函数的计算,并且避免了状态以及变量的概念。<Wiki>

    函数式编程是面向数学的抽象,将计算描述为一种表达式求值,一句话,函数式程序就是一个表达式

    函数式编程最重要的基础是 λ 演算(lambda calculus)。而且λ演算的函数可以接受函数当作输入(参数)和输出(返回值)。和指令式编程相比,函数式编程的函数的计算比指令的执行更重要。和过程化编程相比,函数式编程的函数的计算可随时调用。纯函数式编程不需要变量;

     

    语言族

    函数式编程中最古老的例子可能要数1958年被创造出来的LISP,当年的Lisp由于各种设计缺陷(内存损耗、闭包问题、生成程序执行效率等)没能发展好。较现代的例子包括Haskell、Clean、Erlang 和Miranda等。

    现代编程语言,如C#、Python、Ruby、Scala等等,它们都受到了函数式编程语言的影响,比如C#中的lamada表达式、Linq。

    基于JVM实现的Lisp方言如Scala, Clojure也是越来越受关注,这里所谓的Lisp方言,主要是因为语法上沿用了Lisp中的S表达式。

    基于.net平台的有F#,微软的首个函数式编程语言。<MSDN>

     

    不同语言的抽象层次

        高                                               计算

                  C#         ----->     对象

                  Python      ----->    函数式

                  C语言      ----->    函数 (面向过程)

                  汇编语言

        低     计算机硬件  ----->    指令                 计算机

     

    函数式复兴

    Anders Hejlsberg,C#编程语言的首席架构师,2010年关于《编程语言的发展趋势及未来方向》演讲

    从一个数组中找出所有的偶数

    List<int> list = new List<int> { 1,2,3,4,5,6,7};

    常规的命令式写法:

        List<int> ret = new List<int>();

        foreach (var item in list)

        { if (item % 2 == 0)

              ret.Add(item); }

    声明式的写法: var ret = list.Where((x) => x % 2 == 0);

     

    多核与并行

             使用命令式编程语言写程序时,我们经常会编写如x = x 1这样的语句,此时我们大量依赖的是可变状态,或者说是“变量”,它们的值可以随程序运行而改变。可变状态非常强大,但随之而来的便是被称为“副作用”的问题,例如一个无需参数的void方法,它会根据调用次数或是在哪个线程上进行调用对程序产生影响,它会改变程序内部的状态,从而影响之后的运行效果。而在函数式编程中则不会出现这个情况,因为所有的状态都是不可变的。事实上对函数式编程的讨论更像是数学、公式,而不是程序语句,如x = x 1对于数学家来说,似乎只是个永不为真的表达式而已。

    函数式编程十分容易并行,因为它在运行时不会修改任何状态,因此无论多少线程在运行时都可以观察到正确的结果。假如两个函数完全无关,那么它们是并行还是顺序地执行便没有什么区别。

     

    函数式编程特性                             与技术

         函数是一等公民                        闭包

         高阶函数                                  惰性求值

         递归                                         缓存技术

        不可变状态                               尾调用消除

        柯里化                                      内存回收

     

    C#函数式支持

             Linq涉及的C#语言特性:隐式类型、匿名类型、初始化器、迭代器、委托、泛型、泛型委托、匿名方法、Lamada表达式。

    函数对象必须是某种委托类型. 在C#中,我们可以定义强类型的委托类型或泛型的委托类型,委托可以代表跟这个委托类型有相同参数的方法(静态方法,类方法)的引用.

    在使用LINQ的时候我们可以经常看到高阶函数。举个例子,如果你想将一个已有的序列使用一些函数转换为一个新的序列,你将使用类似LINQ的select函数(函数作为输入):

    var squares = numbers.Select( num => num*num );

     

    函数是一等公民

             对象是面向对象的第一型,那么函数式编程也是一样,函数是函数式编程的第一型。

    我们在函数式编程中努力用函数来表达所有的概念,完成所有的操作。

    在面向对象编程中,我们把对象传来传去,那在函数式编程中,我们要做的是把函数传来传去。

    函数这个术语不是指计算机中的函数,而是指数学中的函数,即自变量的映射。

    函数可以在任何地方定义,在函数内或函数外,可以作为函数的参数和返回值,可以对函数进行组合。

    高阶函数:能接收函数做参数的函数。

    1:函数自身接受一个或多个函数作为输入参数;

    2:函数自身能输出(返回)一个函数;

    不可变状态

       纯函数式编程语言中的变量也不是命令式编程语言中的变量,即存储状态的单元,而是代数中的变量,即一个值的名称。变量的值是不可变的;

       函数即不依赖外部的状态也不修改外部的状态,函数调用的结果不依赖调用的时间和位置,使得单元测试和调试都更容易。

    递归

       由于变量不可变,纯函数编程语言无法实现循环,这是因为For循环使用可变的状态作为计数器,而While循环DoWhile循环需要可变的状态作为跳出循环的条件。因此在函数式语言里就只能使用递归来解决迭代问题,这使得函数式编程严重依赖递归。

    递归定义的计算的Scala代码如下:

    def fact(n: Int):Int= {

    if(n == 0) return 1

    n * fact(n-1)

    }

    C#代码

    Public int Fact(int n)

    {

     int acc = 1;
    

    for(int k = 1; k <= n; k ){

    acc = acc * k;}

    }

    尾递归

    如果一个函数中所有递归形式的调用都出现在函数的末尾,我们称这个递归函数是尾递归的。当递归调用是整个函数体中最后执行的语句且它的返回值不属于表达式的一部分时,这个递归调用就是尾递归。尾递归函数的特点是在回归过程中不用做任何操作,这个特性很重要,因为大多数现代的编译器会利用这种特点自动生成优化的代码(将尾递归转化为迭代);

    柯里化(Currying)和部分(偏)函数(Partial Function)

    是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。

    主要功能是提供了强大的动态函数创建方法,通过调用另一个函数并为它传入要柯里化(currying)的函数和必要的参数而得到。通俗点说就是利用已有的函数,再创建一个动态的函数,该动态函数内部还是通过已有的函数来发生作用。

    柯里化就是一个函数在参数没给全时返回另一个函数,返回的函数的参数正好是余下的参数。比如:你制定了x和y, 如2的3次方,就返回8, 如果你只制定x为2,y没指定, 那么就返回一个函数:2的y次方, 这个函数只有一个参数:y。

    curry就是对高阶函数(就是一种对过程的抽象 参考map它就是一个抽象的过程)的降阶处理。

    2大特性:

    • 匿名函数
    • 每个函数只有1个参数

    将多个参数的函数进行拆分,拆成多个只有一个参数的函数。为什么要拆分,λ 演算。

    示例:

    常规的写法:Func<int, int, int> Add = (x, y) => x y;

    拆分:      Func<int, Func<int, int>> Add = x => y => x y;

    输入一个参数,返回一个具有一个参数的函数,接着再调用返回的函数,就完成整个调用。

    调用:

             var add2 = Add(3);

         var ret = add2(4);

    写成一行:

    var ret = Add(3)(4);

    或者不重写,只要为原来的方法加一个扩展方法:

    public static Func<T1,Func<T2,T3>> Currey<T1,T2,T3>(this Func<T1,T2,T3> func)

    {

        return x => y => func(x,y);

    }

    这样就可以对C#标准的GenralAdd(int x,int y)方法执行Currey转换为部分(偏)函数了:

    Func<int, int, int> Add = GenralAdd;

             var CurreyedAdd = Add.Currey()(3)(4);

    示例:比如我们经常需要执行SQL语句,当然需要使用SqlConnection,然后附加上对应的SQL语句,为此我们可以开发一个简单的函数,用来简化这一过程:

             Func<SqlConnection, Func<String, DataSet>> ExecSql = x => y =>

             {

                  using (x)

                  {

                       x.Open();

                       var com = x.CreateCommand();

                       DataSet ds = new DataSet();

                       com.CommandText = y;

                       SqlDataAdapter adapter = new SqlDataAdapter(com);

                       adapter.Fill(ds);

                       return ds;

                   }

        };

    调用:

             var esql = ExecSql(new SqlConnection("xxx"));

             var rds = esql("select xxxx from xxx");

             rds = esql("select ffff from ffff");

     

    如果想先传入Sql语句再传入SqlConnection:

    Func<String, Func<SqlConnection, DataSet>> ExecSqlT = x => y => ExecSql(y)(x);

    看一个函数:

                 static Func<int, int> GetAFunc()

            {

                var myVar = 1;

                Func<int, int> inc = delegate(int var1)

                {

                    myVar = myVar 1;

                    return var1 myVar;

                };

                return inc;

            }

     

    如下调用输入什么结果:

                var inc = GetAFunc();

                Console.WriteLine(inc(5));

                Console.WriteLine(inc(6));

     

    闭包

             闭包是可以包含自由(未绑定到特定对象)变量的代码块;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)。

    “闭包” 一词来源于以下两者的结合:要执行的代码块(由于自由变量被包含在代码块中,这些自由变量以及它们引用的对象没有被释放)和为自由变量提供绑定的计算环境(作用域)。<百度百科>

     

    C#中实现闭包,实际就是通过类,封装变量和方法,提升生命周期。

            static void Closure1()

            {

                List<Action> actions = new List<Action>();

                for (int i = 0; i < 10; i )

                {

                    int copy = i;

                    actions.Add(() => Console.WriteLine(copy));

                }

                foreach (Action action in actions)

    action();

            }

     

            static void Closure2()

            {

                int copy;

                List<Action> actions = new List<Action>();

                for (int i = 0; i < 10; i )

                {

                    copy = i;

                    actions.Add(() => Console.WriteLine(copy));

                }

                foreach (Action action in actions)

    action();

            }

     

    缓存技术

    怎样使用闭包来实现缓存。如果我们创建了一个用于缓存的接收函数就可以实现缓存,并返回一个在一段时间内缓存结果的新函数。以下列表显示的例子:

                public static Func<T> Cache<T>(this Func<T> func, int cacheInterval)

                {

                    var cachedValue = func();

                    var timeCached = DateTime.Now;

                    Func<T> cachedFunc = () => {

                    if ((DateTime.Now - timeCached).Seconds >= cacheInterval)

                    {

                            timeCached = DateTime.Now;

                            cachedValue = func();

                         }

                         return cachedValue;

                    };

                    return cachedFunc;

                }

    变量 cacheInterval, cachedValue 和 timeCached 绑定到缓存的函数并作为函数的一部分。这个可以让我们记住最后的值并确认被缓存多长时间。

     

    下面的例子中我们可以看到如何使用这个扩展来缓存函数值和返回当前时间:

    Func<DateTime> now = () => DateTime.Now;

                Func<DateTime> nowCached = now.Cache(4);

                Console.WriteLine("tCurrent timetCached time");

                for (int i = 0; i < 20; i )

                {

                    Console.WriteLine("{0}.t{1:T}t{2:T}", i 1, now(), nowCached());

                    Thread.Sleep(1000);

                }

     

    惰性求值

    C#语言小部分采用了非严格求值策略,大部分还是严格求值策略。

    非严格求值的例子:逻辑或

    static void NonStrictEvaluation()

            {

                bool ret = true || DoSomeThing() > 0;

                Console.WriteLine("Done!");

            }

    严格求值策略:首先定义一个返回Int的方法

            static int DoSomeThing()

            {

                Console.WriteLine("DoSomeThing Function Excuted");

                return 7;

            }

    static void StrictEvaluation(bool flag, int dsVal)

            {

                if (flag)

                    Console.WriteLine("dsVal result value is {0}", dsVal);

                Console.WriteLine("Done!");

            }

    调用:StrictEvaluation(false, DoSomeThing());

    输出:

    DoSomeThing Function Excuted

    Done!

    虽然flag为false,但是DoSomeThing还是被执行了,如何改变?

    将第二个参数改成方法:

    static void LazyEvaluation(bool flag,Func<int> dsthing)

            {

                if (flag)

                    Console.WriteLine("dsthing result value is {0}", dsthing());

                Console.WriteLine("Done!");

            }

    调用:StrictEvaluation(false, DoSomeThing);

     

    如果flag为true,并且其中调用两次,那么DoSomeThing就会被执行两次。再次修改

    static void LazyEvaluationEx(bool flag, Func<int> dsthing)

            {

                Lazy<int> lzDshting = new Lazy<int>(dsthing);

                if (flag)

          Console.WriteLine("dsthing square result value is {0}", lzDshting.Value * lzDshting.Value);

                Console.WriteLine("Done!");

            }

    参考

     

     

    编程语言范式 常见的编程范式有 命令式编程( Imperative programming ) , 函数式编程 , 逻辑式编程; 许多现存的编程语言都...

    在理解函数作为一等公民这句话时,让我们先来看一下一种非常常用的互联网语言JavaScript,相信大家对它都不会陌生。JavaScript并不是严格意义上的函数式编程,不过,它也不是属于严格的面向对象。但是,如果你愿意,你既可以把它当做面向对象语言,也可以把它当做函数式语言,因此,称之为多范式语言,可能更加合适。

    如果你使用jQuery,你可能会经常使用如下的代码: 

    $("button").click(function(){  
      $("li").each(function(){  
        alert($(this).text())  
       });  
     });  
    

    注意这里each()函数的参数,这是一个匿名函数,在遍历所有的li节点时,会弹出li节点的文本内容。将函数作为参数传递给另外一个函数,这是函数式编程的特性之一。

     

    再来考察另外一个案例:

    function f1(){  
        var n=1;  
        function f2(){  
          alert(n);  
        }  
        return f2;  
      }  
    var result=f1();  
    result(); // 1  
    

     这也是一段JavaScript代码,在这段代码中,注意函数f1的返回值,它返回了函数f2。在倒数第2行,返回的f2函数并赋值给result,实际上,此时的result就是一个函数,并且指向f2。对result的调用,就会打印n的值。

    函数可以作为另外一个函数的返回值,也是函数式编程的重要特点。

    编程范式是一个由思考问题以及实现问题愿景的工具组成的框架。很多现代语言都是聚范式(或者说多重范式): 他们支持很多不同的编程范式,比如面向对象,元程序设计,泛函,面向过程,等等。

    编程范式是一个由思考问题以及实现问题愿景的工具组成的框架。很多现代语言都是聚范式(或者说多重范式): 他们支持很多不同的编程范式,比如面向对象,元程序设计,泛函,面向过程,等等。

    //摘要
    从这个例子可以看出,函数式程序非常简练,描述做什么,而不是怎么做。

    2.无副作用

    函数的副作用指的是函数在调用过程中,除了给出了返回值外,还修改了函数外部的状态,比如,函数在调用过程中,修改了某一个全局状态。函数式编程认为,函数的副用作应该被尽量避免。可以想象,如果一个函数肆意修改全局或者外部状态,当系统出现问题时,我们可能很难判断究竟是哪个函数引起的问题。这对于程序的调试和跟踪是没有好处的。如果函数都是显式函数,那么函数的执行显然不会受到外部或者全局信息的影响,因此,对于调试和排错是有益的。

    注意:显式函数指函数与外界交换数据的唯一渠道就是参数和返回值,显式函数不会去读取或者修改函数的外部状态。与之相对的是隐式函数,隐式函数除了参数和返回值外,还会读取外部信息,或者可能修改外部信息。

    然而,完全的无副作用实际上做不到的。因为系统总是需要获取或者修改外部信息的。同时,模块之间的交互也极有可能是通过共享变量进行的。如果完全禁止副作用的出现,也是一件让人很不愉快的事情。因此,大部分函数式编程语言,如Clojure等,都允许副作用的存在。但是与面向对象相比,这种函数调用的副作用,在函数式编程里,需要进行有效的限制。

    图片 1

    图片 2

    函数式编程与命令式编程最大的不同其实在于:函数式编程关心数据的映射,命令式编程关心解决问题的步骤

    申明式的(Declarative)

    函数式编程是申明式的编程方式。相对于命令式(imperative)而言,命令式的程序设计喜欢大量使用可变对象和指令。我们总是习惯于创建对象或者变量,并且修改它们的状态或者值,或者喜欢提供一系列指令,要求程序执行。这种编程习惯在申明式的函数式编程中有所变化。对于申明式的编程范式,你不在需要提供明确的指令操作,所有的细节指令将会更好的被程序库所封装,你要做的只是提出你要的要求,申明你的用意即可。

    请看下面一段程序,这一段传统的命令式编程,为了打印数组中的值,我们需要进行一个循环,并且每次需要判断循环是否结束。在循环体内,我们要明确地给出需要执行的语句和参数。

    public static void imperative(){  
             int[]iArr={1,3,4,5,6,9,8,7,4,2};  
             for(int i=0;i<iArr.length;i  ){  
                       System.out.println(iArr[i]);  
             }  
    }  
    

    与之对应的申明式代码如下: 

    public static void declarative(){  
             int[]iArr={1,3,4,5,6,9,8,7,4,2};  
             Arrays.stream(iArr).forEach(System.out::println);  
    }  
    

    可以看到,变量数组的循环体居然消失了!println()函数似乎在这里也没有指定任何参数,在此,我们只是简单的申明了我们的用意。有关循环以及判断循环是否结束等操作都被简单地封装在程序库中。

    函数式编程范式

    函数式编程范式

    //
    一般来说,递归这种方式于循环相比被认为是更符合人的思维的,即告诉机器做什么,而不是告诉机器怎么做。递归还是有很强大的表现力的,比如换零钱问题。

    3.尾递归优化

    递归是一种常用的编程技巧。使用递归通常可以简化程序编码,大幅减少代码行数。但是递归有一个很大的弊病——它总是使用栈空间。但是,程序的栈空间是非常有限的,与堆空间相比,可能相差几个数量级(栈空间大小通常只有几百K,而堆空间则通常达到几百M甚至上百G)。因此,大规模的递归操作有可能发生栈空间溢出错误,这也限制了递归函数的使用,并给系统带来了一定的风险。

    而尾递归优化可以有效地避免这种状况。尾递归指递归操作处于函数的最后一步。在这种情况下,该函数的工作其实已经完成(剩余的工作就是再次调用它自己),此时,只需要简单得将中间结果传递给后继调用的递归函数即可。此时,编译器就可以进行一种优化,使当前的函数调用返回,或者用新函数的帧栈覆盖老函数的帧栈。总之,当递归处于函数操作的最后一步时,我们总是可以想方设法避免递归操作不断申请栈空间。

    大部分函数式编程语言直接或者间接支持尾递归优化。

    函数式编程就像一辆氢燃料驱动的汽车——先进的未来派,但是还没有被广泛推广。与命令式编程相反,他由一系列语句组成,这些语句用于更新执行时的全局状态。函数式编程将计算转化作表达式求值。这些表达式全由纯数学函数组成,这些数学函数都是一流的(可以被当做一般值来运用和处理),并且没有副作用。

    函数式编程就像一辆氢燃料驱动的汽车——先进的未来派,但是还没有被广泛推广。与命令式编程相反,他由一系列语句组成,这些语句用于更新执行时的全局状态。函数式编程将计算转化作表达式求值。这些表达式全由纯数学函数组成,这些数学函数都是一流的(可以被当做一般值来运用和处理),并且没有副作用。

    从这个例子可以看出,函数式程序非常简练,描述做什么,而不是怎么做。

    4.不变模式

    如果读者熟悉多线程程序设计,那么一定对不变模式有所有了解。所谓不变,是指对象在创建后,就不再发生变化。比如,java.lang.String就是不变模式的典型。如果你在Java中创建了一个String实例,无论如何,你都不可能改变整个String的值。比如,当你使用String.replace()函数试图进行字符串替换时,实际上,原有的字符串对象并不会发生变化,函数本身会返回一个新的String对象,作为给定字符替换后的返回值。不变的对象在函数式编程中被大量使用。

    请看以下代码:

    static int[] arr={1,3,4,5,6,7,8,9,10};  
    Arrays.stream(arr).map((x)->x=x 1).forEach(System.out::println);  
    System.out.println();  
    Arrays.stream(arr).forEach(System.out::println);  
    

    代码第2行看似对每一个数组成员执行了加1的操作。但是在操作完成后,在最后一行,打印arr数组所有的成员值时,你还是会发现,数组成员并没有变化!在使用函数式编程时,这种状态是一种常态,几乎所有的对象都拒绝被修改。

    图片 3

    图片 4

    函数式语言当然还少不了以下特性:高阶函数(Higher-order function)
    偏应用函数(Partially Applied Functions)
    柯里化(Currying)
    闭包(Closure)

    5.易于并行

    由于对象都处于不变的状态,因此函数式编程更加易于并行。实际上,你甚至完全不用担心线程安全的问题。我们之所以要关注线程安全,一个很大的原因是当多个线程对同一个对象进行写操作时,容易将这个对象“写坏”,更专业的说法是“使得对象状态不一致”。但是,由于不变模式的存在,对象自创建以来,就不可能发生改变,因此,在多线程环境下,也就没有必要进行任何同步操作。这样不仅有利于并行化,同时,在并行化后,由于没有同步和锁机制,其性能也会比较好。读者可以关注一下java.lang.String对象。很显然,String对象可以在多线程中很好的工作,但是,它的每一个方法都没有进行同步处理。

    函数式编程很重视以下值:

    函数式编程很重视以下值:
    函数是一等要务

    高阶函数就是参数为函数或返回值为函数的函数。有了高阶函数,就可以将复用的粒度降低到函数级别,相对于面向对象语言,复用的粒度更低。
    高阶函数提供了一种函数级别上的依赖注入(或反转控制)机制,在上面的例子里,sum函数的逻辑依赖于注入进来的函数的逻辑。很多GoF设计模式都可以用高阶函数来实现,如Visitor,Strategy,Decorator等。比如Visitor模式就可以用集合类的map()或foreach()高阶函数来替代。

    6.更少的代码

    通常情况下,函数式编程更加简明扼要,Clojure语言(一种运行于JVM的函数式语言)的爱好者就宣称,使用Clojure可以将Java代码行数减少到原有的十分之一。一般说来,精简的代码更易于维护。而Java代码的冗余性也是出了名的,大部分对于Java语言的攻击都会直接针对Java繁琐,而且死板的语法(但我认为这也是Java的优点之一,正如本书第一段提到的“保守的设计思想是Java最大的优势”),然而,引入函数式编程范式后,这种情况发生了改变。我们可以让Java用更少的代码完成更多的工作。

    请看下面这个例子,对于数组中每一个成员,首先判断是否是奇数,如果是奇数,则执行加1,并最终打印数组内所有成员。

    数组定义:

    1. static int[] arr={1,3,4,5,6,7,8,9,10};  
    2. 传统的处理方式:  
    3. for(int i=0;i<arr.length;i ){  
    4.          if(arr[i]%2!=0){  
    5.                    arr[i] ;  
    6.          }  
    7.          System.out.println(arr[i]);  
    8. }  

     

    使用函数式方式:

    Arrays.stream(arr).map(x->(x%2==0?x:x 1)).forEach(System.out::println);

    可以看到,函数式范式更加紧凑而且简洁。

     感兴趣的朋友可以看看这本电子书《Java8函数式编程入门》

     

    函数式编程与面向对象的设计方法在思路和手段上都各有千秋,在这里,我将简要...

    函数是一等要务

    我们应该将函数与编程语言中的其他类对象同样对待。换句话说,您可以将函数存储在变量里,动态创建函数,以及将函数返回或者将函数传递给其他函数。下面我们来看一个例子...

    总结函数式编程是给软件开发者提供的另一套工具箱,为我们提供了另外一种抽象和思考的方式。函数式编程也有不太擅长的场合,比如处理可变状态和处理IO,要么引入可变变量,要么通过Monad来进行封装(如State Monad和IO Monad)

    我们应该将函数与编程语言中的其他类对象同样对待。换句话说,您可以将函数存储在变量里,动态创建函数,以及将函数返回或者将函数传递给其他函数。下面我们来看一个例子...

     图片 5

    //
    函数式编程与命令式编程最大的不同其实在于:函数式编程关心数据的映射,命令式编程关心解决问题的步骤这里的映射就是数学上“函数”的概念——一种东西和另一种东西之间的对应关系。这也是为什么“函数式编程”叫做“函数式编程”。

    图片 6

    一个字符串可以保存为一个变量,函数也可以,例如:

    这就是命令式编程——你要做什么事情,你得把达到目的的步骤详细的描述出来,然后交给机器去运行。这也正是命令式编程的理论模型——图灵机的特点。一条写满数据的纸带,一条根据纸带内容运动的机器,机器每动一步都需要纸带上写着如何达到。那么,不用这种方式,如何翻转二叉树呢?函数式思维提供了另一种思维的途径——所谓“翻转二叉树”,可以看做是要得到一颗和原来二叉树对称的新二叉树。这颗新二叉树的特点是每一个节点都递归地和原树相反。用 haskell 代码表达出来就是:data Tree a = Node a (Maybe (Tree a)) (Maybe (Tree a)) deriving (Show, Eq)invert :: Maybe (Tree a) -> Maybe (Tree a)invert Nothing = Nothinginvert (Just Node v l r) = Just (Node v (invert r) (invert l))

    一个字符串可以保存为一个变量,函数也可以,例如:

    复制代码 代码如下:

    (防止看不懂,翻译成等价的 python )def invert(node): if node is None: return None else return Tree(node.value, invert(node.right), invert(node.left))

    var sayHello = function() { return “Hello” };
    

    var sayHello = function() { return “Hello” };

    这段代码体现的思维,就是旧树到新树的映射——对一颗二叉树而言,它的镜像树就是左右节点递归镜像的树。这段代码最终达到的目的同样是翻转二叉树,但是它得到结果的方式和 python 代码有着本质的差别:通过描述一个 旧树->新树 的映射,而不是描述“从旧树得到新树应该怎样做”来达到目的。那么这样有什么好处呢?首先,最直观的角度来说,函数式风格的代码可以写得很精简,大大减少了键盘的损耗(其次,函数式的代码是“对映射的描述”,它不仅可以描述二叉树这样的数据结构之间的对应关系,任何能在计算机中体现的东西之间的对应关系都可以描述——比如函数和函数之间的映射(比如 functor**);比如外部操作到 GUI 之间的映射(就是现在前端热炒的所谓 FRP)。它的抽象程度可以很高,这就意味着函数式的代码可以更方便的复用。另外还有其他答主提到的,可以方便的并行。同时,将代码写成这种样子可以方便用数学的方法进行研究(这就是为什么可以扯上范畴上的这种数学上的高深概念)
    【至于什么科里化、什么数据不可变,都只是外延体现而已】。

    本文由1010cc时时彩经典版发布于1010cc时时彩经典版,转载请注明出处:而不是怎么做,关于Java8函数式编程你需要了解的

    关键词: