C++,C#,Java中的Lambda

11 篇文章 1 订阅
2 篇文章 0 订阅
本文详细介绍了C++、C#和Java中的Lambda表达式,包括Lambda的定义、捕获列表的使用、表达式lambda和语句lambda的区别,以及Java中Lambda表达式的语法和注意事项。
摘要由CSDN通过智能技术生成

一、C++中的Lambda

1、示例

#include <algorithm>
#include <cmath>

void abssort(float* x, unsigned n) {
    std::sort(x, x + n,
        // Lambda expression begins
        [](float a, float b) {
            return (std::abs(a) < std::abs(b));
        } // end of lambda expression
    );
}

在上面的实例中std::sort函数第三个参数应该是传递一个排序规则的函数,但是这个实例中直接将排序函数的实现写在应该传递函数的位置,省去了定义排序函数的过程,对于这种不需要复用,且短小的函数,直接传递函数体可以增加代码的可读性。

2、定义

1)捕获列表

在C ++规范中也称为Lambda导入器, 捕获列表总是出现在Lambda函数的开始处。实际上,[]是Lambda引出符。编译器根据该引出符判断接下来的代码是否是Lambda函数,捕获列表能够捕捉上下文中的变量以供Lambda函数使用。


2)参数列表

与普通函数的参数列表一致。如果不需要参数传递,则可以连同括号“()”一起省略。


3)可变规格*

mutable修饰符, 默认情况下Lambda函数总是一个const函数,mutable可以取消其常量性。在使用该修饰符时,参数列表不可省略(即使参数为空)。


4)异常说明

用于Lamdba表达式内部函数抛出异常。


5)返回类型

追踪返回类型形式声明函数的返回类型。我们可以在不需要返回值的时候也可以连同符号”->”一起省略。此外,在返回类型明确的情况下,也可以省略该部分,让编译器对返回类型进行推导。


6)lambda函数体

内容与普通函数一样,不过除了可以使用参数之外,还可以使用所有捕获的变量。

3、捕获列表

[] 什么也不捕获,无法lambda函数体使用任何

[=] 按值的方式捕获所有变量

[&] 按引用的方式捕获所有变量

[=, &a] 除了变量a之外,按值的方式捕获所有局部变量,变量a使用引用的方式来捕获。这里可以按引用捕获多个,例如 [=, &a, &b,&c]。这里注意,如果前面加了=,后面加的具体的参数必须以引用的方式来捕获,否则会报错。

[&, a] 除了变量a之外,按引用的方式捕获所有局部变量,变量a使用值的方式来捕获。这里后面的参数也可以多个,例如 [&, a, b, c]。这里注意,如果前面加了&,后面加的具体的参数必须以值的方式来捕获。

[a, &b] 以值的方式捕获a,引用的方式捕获b,也可以捕获多个。

[this] 在成员函数中,也可以直接捕获this指针,其实在成员函数中,[=]和[&]也会捕获this指针。

#include <iostream>

int main()
{
    int a = 3;
    int b = 5;
    
    // 按值来捕获
    auto func1 = [a] { std::cout << a << std::endl; };
    func1();

    // 按值来捕获
    auto func2 = [=] { std::cout << a << " " << b << std::endl; };
    func2();

    // 按引用来捕获
    auto func3 = [&a] { std::cout << a << std::endl; };
    func3();

    // 按引用来捕获
    auto func4 = [&] { std::cout << a << " " << b << std::endl; };
    func4();
}

二、C#中的Lambda

1、概述

使用 Lambda 表达式来创建匿名函数。 使用 lambda声明运算符=> 从其主体中分离 lambda 参数列表。

若要创建 Lambda 表达式,需要在 Lambda 运算符左侧指定输入参数(如果有),然后在另一侧输入表达式或语句块。

任何 Lambda 表达式都可以转换为委托类型。 Lambda 表达式可以转换的委托类型由其参数和返回值的类型定义。 如果 lambda 表达式不返回值,则可以将其转换为 Action 委托类型之一;否则,可将其转换为 Func 委托类型之一。 例如,有 2 个参数且不返回值的 Lambda 表达式可转换为 Action<T1,T2> 委托。 有 1 个参数且不返回值的 Lambda 表达式可转换为 Func<T,TResult> 委托。 以下示例中,lambda 表达式 x => x * x(指定名为 x 的参数并返回 x 平方值)将分配给委托类型的变量:

Func<int, int> square = x => x * x;
Console.WriteLine(square(5));
// Output:
// 25

2、表达式lambda

表达式位于 => 运算符右侧的 lambda 表达式称为“表达式 lambda”。 表达式 lambda 会返回表达式的结果,并采用以下基本形式:

(input-parameters) => expression

表达式 lambda 的主体可以包含方法调用。 不过,若要创建在 .NET 公共语言运行时 (CLR) 的上下文之外(如在 SQL Server 中)计算的表达式树,则不得在 Lambda 表达式中使用方法调用。 在 .NET 公共语言运行时 (CLR) 上下文之外,方法没有任何意义。

3、语句lambda

语句 lambda 与表达式 lambda 类似,只是语句括在大括号中:

(input-parameters) => { <sequence-of-statements> }

语句 lambda 的主体可以包含任意数量的语句;但是,实际上通常不会多于两个或三个。

Action<string> greet = name =>
{
    string greeting = $"Hello {name}!";
    Console.WriteLine(greeting);
};
greet("World");
// Output:
// Hello World!

不能使用语句 Lambda 创建表达式树。

三、Java中的lambda

1、概述

Lambda表达式是JDK1.8之后的一种语法,是一个匿名函数,是对匿名函数的简写形式,我们可以把 Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递),可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升;

首先来看一下什么是Lambda表达式:

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

public class MyTest {
    public static void main(String[] args) {
        Integer[] ints = {98, 243, 35, 13, 57, 243};
        List<Integer> list = Arrays.asList(ints);

        //之前的排序
        list.sort(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2-o1;
            }
        });
        System.out.println(list);
        //[243, 243, 98, 57, 35, 13]

        //使用Lambda表达式
        list.sort((o1,o2)->(o1-o2));

        System.out.println(list);
        //[13, 35, 57, 98, 243, 243]
    }
}

2、语法

JDK1.8之后引入的一种语法,他的写法是使用一个->符号,箭头将Lambda表达式分为左右两部分,左边写的是实现的这个接口中的抽象方法中的形参列表,右边就是对抽象方法的处理;

实现的这个接口中的抽象方法中的形参列表 -> 抽象方法的处理

3、具体实现

因为Lambda表达式的核心就是实现的这个接口中的抽象方法中的形参列表 -> 抽象方法的处理因此根据形参列表与返回值的不同,Lambda表达式的具体写法也不相同;

1)无返回值有形参的抽象方法

public interface MyInterface {
    public abstract void show(int a,int b);
}
public class MyTest1 {
    public static void main(String[] args) {
        MyInterface myInterface = new MyInterface() {
            @Override
            public void show(int a, int b) {
                System.out.println(a + b);
            }
        };

        myInterface.show(20, 30);//50

        //简写1:方法名可以自己推断出来
        MyInterface myInterface1 = (int a, int b) -> {
            System.out.println(a + b);
        };

        myInterface1.show(20, 40);//60

        //简写2:可以省略形参列表中的形参类型
        MyInterface myInterface2 = (a, b) -> {
            System.out.println(a + b);//70
        };

        myInterface2.show(20, 50);

        //简写3:如果抽象方法中只有一行代码,可以省略方法体的大括号,当然,如果不止一行,就不能省略
        MyInterface myInterface3 = (a, b) -> System.out.println(a + b);
        myInterface3.show(20, 60);//80
    }
}
  • 可以省略方法名,IDEA会帮你自动检测方法名;
  • 可以省略方法中的形参类型;
  • 如果对抽象方法的实现逻辑只有一行,可以省略方法体的大括号,当然如果不止一行,就不能省略了;

 2) 有返回值的抽象方法

public interface MyInterface1 {
    public abstract int test(int a,int b);
}
public class MyTest2 {
    public static void main(String[] args) {
        MyInterface1 test1 = new MyInterface1() {
            @Override
            public int test(int a, int b) {
                return a - b;
            }
        };
        System.out.println(test1.test(90, 8));//82

        //简写1:
        MyInterface1 test2 = (int a, int b) -> {
            return a - b;
        };
        System.out.println(test2.test(20, 10));//10

        //简写2:
        MyInterface1 test3 = (a, b) -> {return a - b;};
        System.out.println(test3.test(30, 10));//20

        //简写3:这个有返回值的方法,不能直接去掉大括号,还需要去掉return关键字
        MyInterface1 test4 = (a, b) -> a - b;
        System.out.println(test4.test(40, 10));//30
    }
}
  • 有返回值的方法,如果要去掉大括号,还需要去掉return关键字;

3)有一个形参的抽象方法

public interface MyInterface2 {
    public abstract int show(int a);
}
public class MyTest3 {
    public static void main(String[] args) {
        MyInterface2 myInterface = a -> a-20;
        myInterface.show(20);
    }
}
  • 形参列表中只有一个参数,可以去掉形参的括号;

 4)Lambda表达式作为参数传递

import java.util.Arrays;
public class MyTest4 {
    public static void main(String[] args) {
        Integer[] ints = {89, 67, 23};
        Arrays.sort(ints, (o1, o2) -> o1-o2);
        System.out.println(Arrays.toString(ints));
        //[23, 67, 89]
    }
}

4、注意事项

  • Lambda表达式不是万能的,他需要函数式接口的支持;
  • 可以通过 Lambda 表达式来创建该接口的对象,我们可以在任意函数式接口上使用@FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口;

  • 为什么只能有一个抽象方法,如果有多个抽象方法,这个接口不是函数式接口,简写的时候省略了方法名,IDEA不能知道到底重写的是哪一个方法,不能推断出来;

  • 注解写在接口声明上面,如果不报错,就不是函数式接口;

  • JDK1.8之后,提供了很多函数式接口,作为参数传递;

  • 16
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hiOoo.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值