android 函数式编程

by Anup Cowkur

通过安纳普·考库(Anup Cowkur)

Android开发人员的函数式编程-第3部分 (Functional Programming for Android Developers — Part 3)

In the last post, we learned about immutability and concurrency. In this one, we’ll look at Higher Order Functions and Closures.

在上一篇文章中,我们了解了不变性并发性 。 在这一章中,我们将研究高阶函数闭包。

If you haven’t read part 2, please read it here:

如果您尚未阅读第2部分,请在此处阅读:

Functional Programming for Android Developers — Part 2In the last post, we learned about Purity, Side effects and Ordering. In this part, let’s talk about immutability and…medium.freecodecamp.com

适用于Android开发人员的函数式编程-第2部分 在上一篇文章中,我们了解了纯度,副作用和订购。 在这一部分中,让我们谈谈不可变性和... medium.freecodecamp.com

高阶函数 (Higher Order Functions)

Higher Order Functions are functions that can take functions as parameters and returns functions as results. Cool, huh?

高阶函数是可以将函数作为参数并返回函数作为结果的函数。 酷吧?

But why would anyone wanna do that?

但是,为什么有人要这么做呢?

Let’s take an example. Suppose I want to compress a bunch of files. I want to do this two ways — using ZIP or RAR format. To do this in traditional Java, we would use something like the Strategy Pattern.

让我们举个例子。 假设我要压缩一堆文件。 我想通过两种方式执行此操作-使用ZIP或RAR格式。 为了在传统的Java中做到这一点,我们将使用诸如Strategy Pattern之类的方法

Firstly, I’d make an interface that defines the strategy:

首先,我将创建一个定义策略的接口:

public interface CompressionStrategy {    void compress(List<File> files);}

Then I would implement the two strategies like so:

然后,我将实施两种策略,如下所示:

public class ZipCompressionStrategy implements CompressionStrategy {    @Override public void compress(List<File> files) {        // Do ZIP stuff    }}
public class RarCompressionStrategy implements CompressionStrategy {    @Override public void compress(List<File> files) {        // Do RAR stuff    }}

Then at runtime, I can use one of these strategies:

然后在运行时,我可以使用以下策略之一:

public CompressionStrategy decideStrategy(Strategy strategy) {    switch (strategy) {        case ZIP:            return new ZipCompressionStrategy();        case RAR:            return new RarCompressionStrategy();    }}

That’s a lot of code and ceremony.

那是很多代码和仪式。

All we are trying to do here is try to do two different bits of business logic depending on some variable. Since business logic can’t live on it’s own in Java, we have to dress it up in classes and interfaces.

我们在这里要做的就是尝试根据某个变量来完成两个不同的业务逻辑位。 由于业务逻辑不能依靠Java本身存在,因此我们必须在类和接口中进行修饰。

Wouldn’t it be great if we could directly pass in the business logic? That is, if we could treat functions as variables, could we pass business logic around just as easily as variables and data?

如果我们可以直接传递业务逻辑不是很好吗? 也就是说,如果我们可以将函数视为变量,那么是否可以像传递变量和数据一样容易地传递业务逻辑呢?

This is exactly what higher order functions are for!

正是高阶函数的作用!

Let’s see the same example with Higher Order Functions. I’m going to use Kotlin here, since Java 8 lambdas still involve some ceremony of creating functional interfaces which we’d like to avoid.

让我们看一看高阶函数的例子。 我将在这里使用Kotlin ,因为Java 8 lambda仍然涉及创建我们希望避免的功能接口的仪式

fun compress(files: List<File>, applyStrategy: (List<File>) -> CompressedFiles){    applyStrategy(files)}

The compress method takes two parameters — a list of files and a function called applyStrategy which a function of type List<File> -> CompressedFiles.That is, it’s a function that takes a list of files and returns CompressedFiles.

compress方法有两个参数-文件列表和一个名为applyStrategy的函数,该函数的类型为List<File> -> Compres sedFiles。也就是说,它是一个获取文件列表并returns Compre ssedFiles的函数。

Now we can call compress with any function that takes a list of files and returns compressed files:

现在,我们可以使用任何接受文件列表并返回压缩文件的函数来调用compress

compress(fileList, {files -&gt; // ZIP it})
compress(fileList, {files -&gt; // RAR it})

Better. Much better.

更好。 好多了。

So Higher Order Functions allow us to pass logic around and treat code as data. Neat.

因此,高阶函数使我们能够传递逻辑并将代码视为数据。 整齐。

关闭 (Closures)

Closures are functions that capture their environments. Let’s understand this with an example. Suppose I have a click listener on a view and we want to print some value inside it:

闭包是捕获其环境的功能。 让我们通过一个例子来理解这一点。 假设我在视图上有一个点击侦听器,我们想在其中打印一些值:

int x = 5;view.setOnClickListener(new View.OnClickListener() {    @Override public void onClick(View v) {        System.out.println(x);    }});

Java won’t let us do this since x isn’t final. x has to be final in Java since the click listener can be executed anytime and at the time it is executed, x might not be around anymore or it’s value might have changed. Java forces us to make this variable final to effectively make it immutable.

Java不允许我们这样做,因为x不是最终的。 x必须是Java的最终版本,因为单击侦听器可以随时在执行时执行,所以x可能不再存在或它的值可能已更改。 Java强迫我们将变量最终定为有效变量。

Once it’s immutable, Java will know that x is always going to be 5 whenever the click listener is executed. This system isn’t perfect since x can point to a list which can be mutated even though the reference to the list is the same.

一旦它是不可变的,Java将知道每次执行单击侦听器时x始终为5 。 该系统不是完美的,因为x可以指向一个列表,即使对该列表的引用相同,该列表也可以更改。

Java doesn’t have a mechanism for a function to capture and respond to variables that are outside it’s scope. Java functions cannot capture or close over their environment.

Java没有机制来捕获和响应超出其范围的变量。 Java函数无法捕获或关闭其环境。

Let’s try doing the same thing in Kotlin. We don’t even need an anonymous inner class since we have first class functions in Kotlin:

让我们尝试在Kotlin中做同样的事情。 我们甚至不需要匿名内部类,因为我们在Kotlin中具有一流的功能:

var x = 5view.setOnClickListener { println(x) }

This is perfectly valid in Kotlin. Functions in Kotlin are closures. They can keep track of and respond to updates in their environment.

这在Kotlin中完全有效。 Kotlin中的函数是闭包。 他们可以跟踪并响应环境中的更新。

The first time the click listener is triggered, it will print 5. If we then change the value of x and say x = 9 and trigger the click listener again, it will print 9 this time.

首次触发点击侦听器时,它将打印5 。 如果然后更改x的值并说x = 9并再次触发点击侦听器,则这次将输出9

那么我该怎么处理这些闭包呢? (So what can I do with these closures?)

Closures have many nifty use cases. Anytime you want business logic to respond to some state in the environment, you can use closures.

闭包有很多漂亮的用例。 每当您希望业务逻辑响应环境中的某些状态时,都可以使用闭包。

Suppose you have a click listener on a button that shows a dialog with a bunch of messages to the user. If you don’t have closures, you’d have to initialize a new listener with the new list of messages every time the messages change.

假设您在一个按钮上有一个单击侦听器,该按钮显示了一个对话框,其中包含向用户发送的一堆消息。 如果没有闭包,则每次消息更改时,都必须使用新的消息列表初始化新的侦听器。

With closures, you can store the list of messages somewhere and pass the reference to the list in the listener, like we did above, and the listener will always show the latest set of messages.

使用闭包,您可以将消息列表存储在某处,并将引用传递给侦听器中的列表,就像上面所做的那样,侦听器将始终显示最新的消息集。

Closures can also be used to completely replace objects. This is often used in functional languages where you might need some OOP like behavior and the language doesn’t support them.

封闭件也可用于完全替换对象。 这通常用在功能性语言中,在这些功能性语言中,您可能需要某些OOP之类的行为,而该语言不支持它们。

Let’s see an example:

让我们来看一个例子:

class Dog {    private var weight: Int = 10    fun eat(food: Int) {        weight += food    }    fun workout(intensity: Int) {        weight -= intensity    }}

I have dog that gains weight when we feed it and loses weight when it exercises. Can we describe the same behavior with closures?

我养的狗在喂食时会增重,而在锻炼时会减重。 我们可以用闭包描述相同的行为吗?

fun main(args: Array<String>) {   dog(Action.feed)(5)}
val dog = { action: Action ->    var weight: Int = 10
when (action) {        Action.feed -> { food: Int -> weight += food; println(weight) }        Action.workout -> { intensity: Int -> weight -= intensity; println(weight) }    }}
enum class Action {    feed, workout}

The dog function takes an Action and depending on the action, will either feed the dog or get it to workout. When we call dog(Action.feed)(5) in the main function, the result will be 15. The dog function is taking a feed action and returning another function that will feed the dog. When we pass the value 5 to this returned function, it will increment the dog’s weight to 10 + 5 = 15 and print it out.

dog功能执行一个Action并根据动作来喂狗或进行锻炼。 当我们在main函数中调用dog(Action.feed)(5)时,结果将为15dog函数正在执行feed操作,并返回另一个函数来喂养dog。 当我们将值5传递给此返回函数时,它将使狗的体重增加到10 + 5 = 15并打印出来。

So combining Closures and Higher Order Functions, we can get Objects without OOP.
因此,结合使用闭包和高阶函数,我们可以获得没有OOP的对象。

You probably don’t wanna do this in real code but it’s fun to know it can be done. Indeed, Closures are called the poor man’s objects.

您可能不想在真实代码中执行此操作,但是很高兴知道它可以完成。 确实,封闭被称为穷人的对象

摘要 (Summary)

Higher Order Functions allow us to encapsulate business logic better than OOP in many cases and we can pass them around and treat them as data. Closures capture their surrounding environment and help us use Higher Order Functions effectively.

在许多情况下,高阶函数使我们可以比OOP更好地封装业务逻辑,并且可以将它们传递并将其视为数据。 闭包捕获其周围环境,并帮助我们有效地使用高阶函数。

In the next part, we’ll learn about error handling in a functional way.

在下一部分中,我们将以功能性方式学习错误处理。

If you liked this, click the ? below. I notice each one and I’m grateful for every one of them.

如果喜欢此,请单击“?”。 下面。 我注意到每个人,我感谢每个人。

For more musings about programming, follow me so you’ll get notified when I write new posts.

有关编程的更多信息,请关注我,以便在我撰写新文章时得到通知。

翻译自: https://www.freecodecamp.org/news/functional-programming-for-android-developers-part-3-f9e521e96788/

android 函数式编程

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐