用Python,JS和Java分别进行举例告诉你什么是函数式编程?

作者 : IT 大叔 本文共6945个字,预计阅读时间需要18分钟 发布时间: 2020-10-20

如果仅使用面向对象,可能很难在不同的范例中工作。但是,雇主越来越多地寻找可以利用多种范例来解决问题的程序员。特别是函数式编程由于其解决现代问题的效率和可伸缩性而在专业领域日益普及。但是,如何实现从OOP到FP的跳跃呢?

今天,我们将探讨函数式编程的核心概念,并向您展示如何用Python,JavaScript和Java来实现它们!

这是我们今天要介绍的内容:

  • 什么是函数式编程?
  • 功能编程语言
  • 函数式编程的概念
  • 使用Python进行函数式编程
  • 使用JavaScript进行函数式编程
  • Java函数式编程

什么是函数式编程?

函数式编程是一种声明式编程范例,其中程序是通过应用顺序函数而不是语句来创建的。每个函数都接受一个输入值并返回一个一致的输出值,而不会更改或受程序状态的影响。这些功能可以完成一个操作,并且可以按顺序组合以完成复杂的操作。功能范例产生高度模块化的代码,因为功能可以在程序中重用,并且可以被调用,作为参数传递或返回。

纯函数不会产生副作用,也不依赖于全局变量或状态。

功能编程

当解决方案易于在函数中表达且几乎没有物理意义时,可以使用函数式编程。面向对象的程序在真实世界的对象之后对代码进行建模,而函数式编程则擅长于数学函数,其中中间值或最终值没有物理关联。函数式编程的一些常见用法是AI设计,ML分类算法,财务程序或高级数学函数模型。

简化:功能程序按顺序执行许多纯的,单一目的的功能,以解决复杂的数学或非物理问题。

功能编程的优点

  • 易于调试:纯函数和不可变数据使查找设置变量值的位置变得容易。纯函数具有较少影响它们的因素,因此使您更容易找到错误的部分。
  • 延迟评估:功能程序仅在需要时评估计算。这使程序可以重用先前计算的结果并节省运行时间。
  • 模块化:纯函数不依赖外部变量或状态来起作用,这意味着它们很容易在整个程序中重用。而且,函数将仅完成一个操作或计算,以确保您可以重用该函数而不会意外导入额外的代码。
  • 增强的可读性:功能性程序易于阅读,因为每个功能的行为都是不可变的,并且与程序状态隔离。结果,您可以仅通过名称就可以预测每个功能经常执行的操作!
  • 并行编程:使用函数式编程方法可以更轻松地创建并行程序,因为不可变的变量会减少程序中的更改量。每个功能仅需处理用户输入,并且可以相信程序状态将保持基本相同!

功能编程语言

并非所有的编程语言都支持功能编程。某些语言(例如Haskell)被设计为功能编程语言。其他语言(例如JavaScript)具有功能和OOP功能,而其他语言则根本不支持功能编程。

功能编程语言:

  • Haskell:这是函数式编程最喜欢的语言。它具有内存安全性,具有出色的垃圾回收功能,并且由于早期的机器代码编译而速度很快。Haskell的丰富且静态的键入系统使您可以访问独特的代数和多态类型,从而使函数式编程更高效且更易于阅读。
  • Erlang:这种语言及其后代Elixir已经确立了利基市场的地位,成为并发系统的最佳功能语言。尽管不如Haskell流行或广泛使用,但它经常用于后端编程。最近,Erlang在可扩展消息传递应用程序(如Whatsapp和Discord)上获得了吸引力。
  • Clojure:此语言是Java虚拟机(JVM)上使用的Lisp的功能优先方言。它是一种主要的功能语言,支持可变和不变的数据结构,但功能上不如这里严格。如果您喜欢Lisp,就会喜欢Clojure。
  • F#:F#与Haskell相似(它们属于同一语言组),但是功能较差。它还对面向对象的构造有少量支持。

功能语言:

  • Scala: Scala支持OOP和函数式编程。它最有趣的功能是类似于Haskell的强大的静态键入系统,可帮助创建强大的功能程序。Scala旨在解决Java批评,因此对于想要尝试函数式编程的Java开发人员来说是一种很好的语言。
  • JavaScript:虽然不是功能优先的,但是JS由于其异步特性而在功能编程方面脱颖而出。JavaScript还支持基本的功能编程功能,例如lambda表达式和解构。这些属性一起将JS标记为其他多范式语言中功能编程的顶级语言。
  • Python,PHP,C ++:这些多范例语言支持功能编程,但与Scala和JavaScript相比,支持不完整。
  • Java:Java是一种通用语言,但它是基于类的OOP的最前沿。lambda表达式的添加使您能够以有限的方式追求更加实用的样式。Java最终是一种OOP语言,可以实现功能编程,但是缺少关键功能,因此值得一筹。

函数式程序设计的概念

设计功能程序时会考虑一些核心概念。

变量和函数

功能程序的核心构建块是变量和函数,而不是对象和方法。您应该避免使用全局变量,因为可变的全局变量会使程序难以理解并导致功能不纯。

纯功能

纯函数具有两个属性:

  • 他们没有副作用
  • 如果给出相同的输入,它们总是产生相同的输出

如果函数更改程序状态,覆盖输入变量或通常在生成输出时进行任何更改,则会引起副作用。纯函数的错误较少,因为副作用使程序的状态复杂化。

引用透明性是指任何函数输出都可以用其值替换,而无需更改程序结果。此概念可确保您创建仅完成单个操作并获得一致输出的功能。

仅当函数不影响程序状态或通常尝试完成多个操作时,引用透明性才可能。

不变性与状态

不变的数据或状态一旦设置就不能更改,并且可以为函数的输出提供稳定的环境。最好的做法是,对每个函数进行编程以产生相同的结果,而不管编程状态如何。如果确实依赖状态,则该状态必须是不可变的,以确保函数输出保持恒定。

函数式编程方法通常避免共享状态函数(多个函数依赖于相同状态)和变异状态函数(函数依赖于可变函数),因为它们会使程序的模块化程度降低。如果必须使用共享状态函数,请将其设为不可变状态。

递归

面向对象的程序设计和函数式程序设计之间的主要区别在于,函数式程序避免使用诸如If-Else语句或循环的构造,这些构造可以在每次执行时创建不同的输出。

功能程序使用递归代替循环来执行所有迭代任务。

一流的功能

函数式编程中的函数被视为数据类型,并且可以像其他任何值一样使用。例如,我们用函数填充数组,将其作为参数传递,或将其存储在变量中。

高阶函数

高阶函数可以接受其他函数作为参数,也可以将返回函数作为输出。高阶函数使我们在进行函数调用和对动作进行抽象时拥有更大的灵活性。

功能组成

可以顺序执行功能以完成复杂的操作。每个函数的结果作为参数传递给下一个函数。这样,您只需一个函数调用就可以调用一系列函数。

使用Python进行函数式编程

Python对函数式编程作为一种多范式语言提供了部分支持。使用功能性方法可以更轻松地完成一些数学程序的Python解决方案。

开始使用功能方法时,最困难的转变是减少使用的类数。Python中的类具有可变的属性,这使得创建纯的,不变的函数变得很困难。尝试将大多数代码保留在模块级别,并仅在需要时才切换到类。让我们看看如何在Python中实现纯净,不变的函数和一流的函数。然后,我们将学习组成函数的语法。

纯而不变的功能

默认情况下,Python的许多内置数据结构是不可变的:

  • 整数
  • 浮动
  • 布尔值
  • 统一码
  • 元组

元组作为数组的不变形式特别有用。

# Python code to test that  
# tuples are immutable  
    
tuple1 = (0, 1, 2, 3)  
tuple1[0] = 4
print(tuple1)

此代码会导致错误,因为它尝试重新分配不可变的元组对象。功能性Python程序应经常使用这些不可变的数据结构来实现纯功能。

以下是纯函数,因为它没有副作用,并且将始终返回相同的输出:

def add_1(x):
    return x + 1

一流的功能

函数在Python中被视为对象。这是我们有关如何在Python中使用函数的快速指南:

作为对象

def shout(text): 
    return text.upper()

通过功能作为参数

def shout(text): 
    return text.upper() 
 
def greet(func): 
    # storing the function in a variable 
    greeting = func("Hi, I am created by a function passed as an argument.") 
    print greeting  
  
greet(shout) 

从另一个函数返回函数

def create_adder(x): 
    def adder(y): 
        return x+y 
  
    return adder 

功能组成

要使用Python编写函数,我们将使用lambda function调用。这使我们可以在一个调用中调用任意数量的参数。

import functools
 
def compose(*functions):
    def compose2(f, g):
        return lambda x: f(g(x))
    return functools.reduce(compose2, functions, lambda x: x)

第4行,我们将定义一个函数compose2,该函数需要两个函数作为参数fg。在第5行,我们返回一个代表组成的新功能fg

最后,在第6行,我们返回组合函数的结果。

JavaScript中的函数式编程

由于对一流功能的支持,JavaScript长期以来一直提供功能功能。函数式编程最近在JavaScript中变得越来越流行,因为在Angular和React之类的框架中使用函数式编程可以提高性能。

让我们看一下如何使用JavaScript实现不同的功能概念。我们将重点介绍如何创建核心概念。纯函数,一流函数和函数组成。

纯而不变的功能

要开始创建在JavaScript纯函数,我们将不得不使用的共同行为,如功能性替代品constconcatfilter()

所述let关键字设置一个可变变量。const相反,声明with可以确保变量是不可变的,因为它可以防止重新分配。

const heightRequirement = 46;
 
function canRide (height){
    return height >= heightRequirement;
}

我们还需要使用功能性替代方案来操纵数组。该push()方法是将元素追加到数组的常用方法。不幸的是,push()修改了原始数组,因此是不纯的。

相反,我们将使用等效的功能concat()。此方法返回一个包含所有原始元素以及新添加的元素的新数组concat()。使用时,原始数组不会被修改。

const a = [1, 2]
const b = [1, 2].concat(3)

要从数组中删除项目,我们通常使用pop()slice()方法。但是,它们无法正常工作,因为它们会修改原始数组。相反,我们将使用filter()来创建一个新数组,其中包含所有通过条件测试的元素。

const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];
 
const result = words.filter(word => word.length > 6);

一流的功能

JavaScript默认情况下支持一流的功能。这是我们可以使用JavaScript中的函数的快速指南。

将函数分配给变量

const f = (m) => console.log(m)
f('Test')

向数组添加函数

const a = [
  m => console.log(m)
]
a[0]('Test')

将函数作为参数传递

const f = (m) => () => console.log(m)
const f2 = (f3) => f3()
f2(f('Test'))

从另一个函数返回函数

const createF = () => {
  return (m) => console.log(m)
}
const f = createF()
f('Test')

功能组成

在JavaScript中,我们可以使用链式函数调用来构成函数:

obj.doSomething()
   .doSomethingElse()

另外,我们可以将函数执行传递给下一个函数:

obj.doSomething(doThis())

如果我们想组成更多的函数,我们可以lodash用来简化组成。具体来说,我们将使用先compose提供参数然后再提供功能列表的功能。列表中的第一个函数使用原始参数作为输入。后面的函数从其前面的函数的返回值继承输入参数。

import { compose } from 'lodash/fp'
 
const slugify = compose(
  encodeURIComponent,
  join('-'),
  map(toLowerCase),
  split(' ')
)
 
slufigy('Hello World') // hello-world

Java函数式编程

Java并不像Python或JavaScript那样真正支持功能编程。但是,我们可以使用lambda函数,流和匿名类来模仿Java中的函数编程行为。最终,Java编译器并不是在考虑函数式编程的情况下创建的,因此无法获得函数式编程的许多好处。

纯而不变的功能

Java的几种内置数据结构是不可变的:

  • 整数
  • 布尔值
  • 字节

您还可以使用final关键字创建自己的不可变类。

// An immutable class 
public final class Student 
{ 
    final String name; 
    final int regNo; 
  
    public Student(String name, int regNo) 
    { 
        this.name = name; 
        this.regNo = regNo; 
    } 
    public String getName() 
    { 
        return name; 
    } 
    public int getRegNo() 
    { 
        return regNo; 
    } 
} 

final上class关键字防止孩子班级的建设。在finalnameregNo使其无法对象的构造之后更改值。该类还具有参数化的构造函数,所有变量的getter方法,并且没有setter方法,每个方法都有助于使它成为不可变的类。

一流的功能

Java可以使用lambda函数来实现一流的功能。Lambda接受表达式列表,例如方法,但不需要名称或预先定义。我们可以使用lambda表达式代替函数,因为它们被视为可以传递或返回的标准类对象。

// FIRST-CLASS
Supplier<String> lambda = myObject::toString;
// HIGHER-ORDER
Supplier<String> higherOrder(Supplier<String> fn) {
    String result = fn.get();
    return () -> result;
}

功能组成

Java包含一个接口,该接口 java.util.function.Function提供了功能组合的方法。该compose方法首先执行传递的函数(multiplyByTen),然后将返回值传递给外部函数(square)。该andThen方法首先执行外部功能,然后执行其参数内的功能。

Function<Integer, Integer> square = (input) -> input * input;
Function<Integer, Integer> multiplyByTen = (input) -> input * 10;
 
// COMPOSE: argument will be run first
Function<Integer, Integer> multiplyByTenAndSquare = square.compose(multiplyByTen);
 
// ANDTHEN: argument will run last
Function<Integer, Integer> squareAndMultiplyByTen = square.andThen(multiplyByTen);

在1和2行,我们首先创建两个函数,squaremultiplyByTen。接着,在线路5和8中,我们使2层复合物的功能multiplyByTenAndSquaresquareAndMultiplyByTen每个都有两个参数(以满足square)。这些复合功能分别完成两个原始功能,但顺序不同。现在,您可以调用复合函数以在同一输入上执行两个原始函数。

免责声明:
1. 本站资源转自互联网,源码资源分享仅供交流学习,下载后切勿用于商业用途,否则开发者追究责任与本站无关!
2. 本站使用「署名 4.0 国际」创作协议,可自由转载、引用,但需署名原版权作者且注明文章出处
3. 未登录无法下载,登录使用金币下载所有资源。
IT小站 » 用Python,JS和Java分别进行举例告诉你什么是函数式编程?

常见问题FAQ

没有金币/金币不足 怎么办?
本站已开通每日签到送金币,每日签到赠送五枚金币,金币可累积。
所有资源普通会员都能下载吗?
本站所有资源普通会员都可以下载,需要消耗金币下载的白金会员资源,通过每日签到,即可获取免费金币,金币可累积使用。

发表评论