面向对象编程与面向过程编程和函数式编程之间的区别

目录

1.引言

2.面向过程编程

3.面向对象编程和面向过程编程的对比

4.函数式编程

5.面向对象编程和函数式编程的对比


1.引言

        面向对象编程是现在流行的编程范式(编程风格)。实际上,除面向对象编程以外,大家熟悉的编程范式还有另外两种:面向过程编程和函数式编程。随着面向对象编程的出现,面向过程编程已经逐渐退出了历史舞台,函数式编程目前还没有被程序员广泛接受,只能作为面向对象编程的补充。为了更好地理解面向对象编程,我们在本节中补充讲解面向过程编程和函数式编程,并且将面向对象编程与面向过程编程和函数式编程进行对比。

2.面向过程编程

        什么是面向过程编程语言?实际上,我们可以通过对比面向对程和面向对象编程语言这两个概念来理解它们。类比面向对象编程与面向对象编程的定义,面向过程编程和面向过程编程语言的定义如下。

        1)面向过程编程也是一种编程范式或编程风格。它以过程(可以理解为方法、函数和操作)作为组织代码的基本单元,以数据(可以理解为成员变量、属性)与方法相分离为主点。面向过程编程风格是一种流程化的编程风格,通过拼接一组顺序执行的方法来操作数据实现一项功能。

        2)面向过程编程语言的主要特点是不支持类和对象这两个语法概念,不支持丰富的面对象编程特性(如继承、多态和封装),仅支持面向过程编程。

        不过,这里必须声明一点,就像之前提到的面向对象编程和面向对象编程语言没有官方义一样,这里给出的面向过程编程和面向过程编程语言的定义也并不是严格的官方定义。之以给出这样的定义,只是为了与面向对象编程和面向对象编程语言进行对比,方便读者理解们之间的区别。

        因为定义不是很严格,也比较抽象,所以我们再用一个例子进一步解释。假设有一个记录了用户信息的文本文件users.txt,每行文本的格式为name&age&gender(如小王&28&男)我们希望编写一个程序,从uers.txt文件中逐行读取用户信息,然后将其格式化为name\tag\tgender(其中,\t是分隔符)这种文本格式,并且按照 age对用户信息进行从小到大排序之后重新将其写入另一个文本文件fommatted_users.txt中。针对这样一个功能需求,我们分析以下利用面向过程编程和面向对象编程这两种编程风格编写的代码有什么不同。

        我们先看一下利用面向过程编程这种编程风格编写的代码是什么样子的。注意,下面这代码是利用C语言这种面向过程编程语言编写的。

struct User{
    char name[64];
    int age;
    char gender[16];
};

struct User parse_to_user(char* text){
    //将文本("小王&28&男”)解析成结构体User
}

char* format_to_text(struct User user) {
    //将结构体user格式化为文本(小王\t28\t男)
}

void sort_users_by_age(struct User user[]){
    //按照年龄大小排序
}

void format_user_file(char* origin_file_path, char* new_file_path){
    //省略打开文件的代码
    struct User user[1024];
    int count = 0;
    while (true){
        struct User user = parse_to_user(line);
        users[count++] = user;
    }
    
    sort_users_by_age(users);

    for (int i = 0; i < count; i++){
        char* formatted_user_text = format_to_text(users[i]);
        //此处省略写入新文件的代码
    }
    //省略关闭文件的代码
}

int main(char** args, int argv){    
    format_user_fle("/home/zheng/users.txt","/home/zheng/formatted users.txt");
    return 0;
}

        我们再看一下利用面向对象编程这种编程风格编写的代码是什么样子的。注意,下面这段代码是利用Java这种面向对象编程语言编写的。

public class User {
    private String name;
    private int age;
    private String gender;

    public User(String name,int age,String gender){
        this.name = name;    
        this.age = age;
        this.gender = gender;
    }

    public static User praseFrom(String userInfoText){
        //将文本("小王&286男")解析成类User
    }

    public String formatToText(){
        //将类User格式化为文本("小王\t28\t男”)
    }
}

public class UserFileFormatter{
    public void format(String userFile,String formatteduserFile){
        //此处省略打开文件的代码
        List users =new Arraylist<>();
        while(1){
            //将文件中的数据读取到userText
            User user =User.parseFrom(userText);
            users.add(user);
        }
        /此处省略按照年龄从小到大排序users的代码
        for(int i= 0:i <users.size(); ++i){
            String formattedUserText = user.formatToText();
            //此处省略写入新文件的代码
        }

        //此处省略关闭文件的代码
    }
}

public class MainApplication{
    public static void main(sring[] args){
        UserFileformatter userFileFormatter =  new UserFileFormatter();
        userFileFormatter.fomnat("/home/zheng/users.txt", " /home/zheng/formatted_users.txt");
    }
}

        从上述两段代码中,我们可以看出,面向过程编程和面向对象编程的基本区别就是的组织方式不同。面向过程编程风格的代码被组织成一组方法的集合及其数据结构(如User),并且方法和数据结构的定义是分开的。面向对象编程风格的代码被组织成一组类。方法和数据结构被绑定在一起,定义在类中。

        分析完上面两段代码,一些读者可能会问,面向对象编程和面向过程编程的区别就这吗?当然不是,关于这两种编程风格的更多区别,请读者继续往下看。

3.面向对象编程和面向过程编程的对比

        在上面章节中,我们介绍了面向过程编程和面向过程编程语言的定义,并将它们与面向对象编程和面向对象编程语言进行了对比。接下来,我们介绍一下面向对象编程为什么能够取代面向过程编程,并成为目前主流的编程范式。相比面向过程编程,面向对象编程有哪些优势?

        (1)面向对象编程更加适合应对大规模复杂程序的开发

        通过上面章节中格式化文本文件的例子,读者可能感觉两种编程范式实现的代码相差不多无非就是代码的组织方式有区别,没有感受到面向对象编程的明显优势。之所以一些读者有这种感受,主要是因为这个例子的程序比较简单,不够复杂。

        对于简单程序的开发,无论是使用面向过程编程风格,还是使用面向对象编程风格,二者实现的代码的差别确实不会很大,有时,甚至面向过程编程风格更有优势,因为需求相当简单,整个程序的处理流程只有一条主线,很容易被划分成顺序执行的几个步骤,然后逐个步骤翻译成代码,这就非常适合采用面向过程这种“面条”式的编程风格。

        但对于复杂的大规模程序的开发,整个程序的处理流程错综复杂,并非只有一条主线。如果我们把整个程序的处理流程画出来,那么它会是一个网状结构。此时,如果我们再用面向过程编程这种流程化、线性的思维方式“翻译”这个网状结构,以及思考如何把程序拆解成一组顺序执行的方法,就会比较吃力。这个时候,面向对象编程风格的优势就体现出来了。

        在面向对象编程中,我们以类为思考对象。在进行面向对象编程时,我们并不是一开始思考如何将复杂的流程拆解为一个个方法,而是采用“曲线救国”的策略,先思考如何给业务建模、如何将需求翻译为类和如何在类之间建立交互关系,而完成这些工作完全不需要考虑错综复杂的处理流程。当我们有了类的设计之后,再像搭积木一样,按照处理流程,将类进行组装,形成整个程序。这种开发模式和思考问题的方式能够让我们在应对复杂程序开发时的思路更加清晰。

        除此之外,面向对象编程还提供了一种模块化的代码组织方式。例如,一个电商交易系统的业务逻辆复杂,代码量很大,我们可能要定义数百个函数、数百个数据结构,如何分门别类地组织这些函数和数据结构,才能让它们不会看起来凌乱呢?类是一种非常好的组织这些函数和数据结构的方式,也是一种将代码模块化的有效手段。

        读者可能会说,对于C语言这种面向过程编程语言,我们可以按照功能的不同,把函数和数据结构放到不同的文件里,以达到给函数和数据结构分类的目的,也可以实现代码的模块化。这样说是没错的,只不过面向对象编程本身提供了类的概念,按照类模块化代码是强制进行的,而面向过程编程语言并不强求以何种方式组织代码。

        实际上,利用面向过程编程语言,我们照样可以写出面向对象编程风格的代码,只不过可能比用面向对象编程语言来写面向对象编程风格的代码付出的代价要高一些。而且,面向过程编程和面向对象编程并非完全对立。在很多软件开发中,尽管我们利用的是面向过程编程语言,但也借鉴了面向对象编程的一些优点。

        (2)面向对象编程风格的代码易复用、易扩展和易维护

        在上述文本文件处理的例子中,因为其代码比较简单,所以我们只用到了类、对象这两个基本的面向对象概念,并没有用到高级的四大特性:封装、抽象、继承和多态。面向对象编程的优势其实并没有发挥出来。

        面向过程编程是一种非常简单的编程风格,并没有像面向对象编程那样提供丰富的特性。而面向对象编程提供的封装、抽象、继承和多态特性,能够极大地满足复杂的编程需求,能够方便我们写出易复用、易扩展和易维护的代码,理由有如下4点。

        1)首先,我们来看封装特性。封装特性是面向对象编程与面向过程编程的基本区别,因为封装基于面向对象编程中的基本概念:类。面向对象编程通过类这种组织代码的方式,将数据和方法绑定在一起,通过访问权限控制,只允许外部调用者通过类暴露的有限方法访问数据,而不会像面向过程编程那样,数据可以被任意方法随意修改。因此,面向对象编程提供的封装特性更有利于提高代码的易维护性。

        2)其次,我们来看抽象特性。我们知道,函数本身就是一种抽象,它隐藏了具体的实现。

        在使用函数时,我们只需要了解函数具有什么功能,不需要了解它是怎么实现的。在这一点上,无论是面向过程编程还是面向对象编程,都支持抽象特性。不过,面向对象编程还提供了其他抽象特性的实现方式。这些实现方式是面向过程编程不具备的,如基于接口实现抽象特性。基于接口的抽象,可以在不改变原有实现的情况下,轻松替换新的实现逻辑,提高了代码的可扩展性。

        3)再次,我们来看继承特性。继承特性是面向对象编程相比面向过程编程所特有的两个特性之一(另外一个是多态)。如果两个类有一些相同的属性和方法,我们就可以将这些相同的代码抽取到父类中,让两个子类继承父类。这样,两个子类就可以重用父类中的代码,避免了代码重复编写,提高了代码的复用性。

        4)最后,我们来看多态特性。基于这个特性,在需要修改一个功能实现时,可以通过实现一个新的子类的方式,在子类中重写原来的功能逻辑,用子类替换父类。在实际的代码运行过程中,调用子类新的功能逻辑,而不是在原有代码上做修改。这样,我们就遵守了“对修改关闭、对扩展开放”的设计原则,提高了代码的扩展性。除此之外,利用多态特性,不同类的对象可以传递给相同的方法,复用同样的逻辑,提高了代码的复用性。

        所以说,基于这四大特性,利用面向对象编程,我们可以轻松地写出易复用、易扩展和易维护的代码。当然,我们不能认为利用面向过程编程方式就不可以写出易复用、易扩展和易维护的代码,但没有四大特性的帮助,付出的代价可能要高一些。

        (3)面向对象编程语言更加人性化、高级和智能

        人最初与机器“打交道”是通过0、1这样的二进制指令,后来,使用的是汇编语言,再后来,使用的是高级编程语言。在高级编程语言中,面向过程编程语言又早于面向对象编程语言出现。之所以先出现面向过程编程语言,是因为与机器交互的方式从二进制指令、汇编语言,逐步发展到面向过程编程语言,这是一种自然的过波,而且它们都属于流程化、“面条”式的编程风格,即用一组指令顺序操作数据来完成一项任务。

        从二进制指令到汇编语言,再到面向过程编程语言,与机器交互的方式在不停演进,从我们可以容易地发现一条规律,那就是编程语言越来越人性化,使得人与机器的交互变得趋越容易。笼统来说,编程语言越来越高级。实际上,在面向过程编程语言之后,面向对象语言的出现也顺应了这样的发展规律,也就是说,面向对象编程语言比面向过程编程语言更高级!

        与二进制指令、汇编语言和面向过程编程语言相比,面向对象编程语言的编程套路、处理问题的方式是完全不一样的。前三者使用的是计算机思维方式,而面向对象编程语言使用的人类思维方式。在使用前3种语言编程时,我们是在思考如何设计一组指令,并“告诉”机器去执行这组指令,操作某些数据,完成某个任务。而在进行面向对象编程时,我们是在思考何给业务建模,以及如何将真实世界映射为类,这让我们能够聚焦于业务本身,而不是思考何与机器打交道。可以这么说,编程语言越高级,离机器越“远”,离我们人类越“近”,也就越“智能”。

        接着上述编程语言的发展规律,如果一种具有突破性的新的编程语言出现,那么它肯定更加“智能”。我们大胆想象一下,如果使用这种编程语言,那么我们可以对计算机知识没有任何了解,无须像现在这样一行行地编写代码,只需要写清楚需求文档,编程语言就能自动生成我们想要的软件。

4.函数式编程

        函数式编程并非新事物,它于50多年前就已经出现了。近几年,函数式编程开始重新被人关注,一些非函数式编程语言加入了很多特性、语法和类库来支持函数式编程,如JavaPython、Ruby和JavaScript等。

        什么是函数式编程(Functional Programming)?

        前面讲到,面向过程编程、面向对象编程并没有严格的官方定义。在当时的讲解中,作着只是给出了自己总结的定义。而且,当时给出的定义也只是对两种编程范式主要特性的总结并不是很严格。实际上,函数式编程也是如此,它也没有一个严格的官方定义。因此,作者就从特性方面定义函数式编程。

        严格来讲,函数式编程中的“函数”并不是指编程语言中的“函数”,而是指数学中的“函数”或“表达式”(如y=f(x))。不过,在编程实现时,对于数学中的“函数”或“表达式”我们习惯地将它们设计成函数。因此,如果不深究的话,那么的函数式编程中的函数也可以理解为编程语言中的“函数”。

        每种编程范式都有其独特的地方,这就是它们会被抽象出来并作为一种范式的原因。面向对象编程最大的特点是以类、对象作为组织代码的单元以及它的四大特性。面向过程编程最大的特点是以函数作为组织代码的单元,数据与方法分离。函数式编程独特的地方是他的编程思想。函数式编程”认为”,程序可以用一系列数学函数或表达式的组合来表示。不过,真的可以把任何程序都可以表示成一组数学表达式吗?

        从理论上来讲,这是可以的。但是,并不是所有的程序都适合这样做。函数式编程有它适合的应用场景,如科学计算、数据处理和统计分析等。在这些应用场景中,程序往往容易用数据表达式来表示。在实现同样的功能时,相比非函数式编程,函数式编程需要的代码更少。但是,对于强业务相关的大型业务系统开发,如果我们费力地将它抽象成数学表达式,非要用函数式编程来实现,那么显然是自讨苦吃。在强业务相关的大型业务系统开发场景下,使用面向对象编程更为合适,因为写出来的代码更具可读性和可维护性。

        上面介绍的是函数式编程的编程思想,具体到编程实现,函数式编程与面向过程编程一样,也是以函数作为组织代码的单元。不过,它与面向过程编程的区别在于,它的函数是无状态的。何为无状态?简单来说,函数内部涉及的变量都是局部变量,不像面向对象编程,共享类成员变量,也不像面向过程编程,共享全局变量。函数的执行结果只与入参有关,与其他任何外部变量无关。同样的入参,无论怎么执行,得到的结果都是一样的。我们举个例子来解释

        下面的 imcrease()函数是有状态函数,执行结果依赖b的值,即便入参相同,多次执行函数,函数的返回值有可能不同,因为b的值有可能不同。

int b;
int increase(int a){
    return a+b;
}

        下面的imcrease()函数是无状态函数,执行结果不依赖任何外部变量值,只要入参相同,无论执行多少次,函数的返回值都相同。

int increase(int a,int b) {
    return a+b;
}

        前面讲到,实现面向对象编程不一定非得使用面向对象编程语言,同理,实现函数式编程也不一定非得使用函数式编程语言。现在,很多面向对象编程语言提供了相应的语法、类库来支持函数式编程。接下来,我们介绍一下Java这种面向对象编程语言对函数式编程的支持,借此加深读者对函数式编程的理解。我们先看下面这段典型的Java函数式编程的代码。

public class FPDemo{
    public static void main(stringl] args){
        Optional<Integer> result = Stream.of("foo", "bar", "hello")
                 .map(s-> s.length())
                 .filter(l ->l <=3)
                 .max((o1, o2) -> o1 - o2);
        System.out.printIn(result.get());//输出2
    }
}

        这段代码的作用是从一个字符串数组中过滤出字符长度小于或等于3的字符串,并且求其中最长字符串的长度。如果读者不了解Java函数式编程的语法,那么可能对上面这段代码感觉有些懵,因为Java为函数式编程引入了3个新的语法概念:Stream 类、Lambd表达式和函数接口(functional interface)。其中,Steam 类的作用是通过它支持用“.”级联多个函数操作的代码编写方式;Lambda表达式的作用是简化代码的编写;函数接口的作用是让我们可以把函数包裹成函数接口,把函数当做参数一样使用(Java不像C支持函数指针那样可以把函数直接当参数来使用)。接下来,我们详细讲解这3个概念。

        (1)Stream类

        假设我们要计算表达式:(3-1)x2+5。如果按照普通的函数调用的方式来编写代码,那么

代码如下。

    add (multiply(subtract (3,1),2), 5)

        这样编写的代码的可读性不好,我们换个可读性更好的写法,如下所示。

    subtract(3,1).multiply(2).add(5);

        我们知道,在Java 中,“.”表示调用关系,即某个对象调用了某个方法。为了支持上的种级联调用方式,我们让每个函数都返回一个通用类型:Stream类对象。在Stream 类上的操作有两种:中间操作和终止操作。中间操作返回的仍然是Stream类对象,而终止操作返园是确定的结果值。

        我们再来看之前的FPDemo类。我们为FPDemo类这段代码添加了注释,如下所示,中,map、filter是中间操作,返回Stream类对象,可以继续级联其他操作;max是终止操作,返回的不是 Stream 类对象,无法继续往下进行级联处理了。具体返回什么类型的数据是由数本身定义的。

public class FPDemo{
    public static void main(stringl] args){
        Optional<Integer> result = Stream.of("foo", "bar", "hello")
                 .map(s-> s.length())//map返回 Stream<Integer>对象
                 .filter(l ->l <=3)  //filter返回 Stream<Integer>对象
                 .max((o1, o2) -> o1 - o2); //max终止操作,返回Optional<Integer>
        System.out.printIn(result.get());//输出2
    }
}

        (2)Lambda 表达式

        前面讲到,引入Lambda表达式的主要作用是简化代码的编写。我们用map函数举例说明。下面列出3段代码,第一段代码展示了map函数的定义,map函数接收的参数是一个Function接口,也就是后续要讲到的函数接口:第二段代码展示了map函数的使用方式;第三段代是使用Lambda表达式对第二段代码简化之后的写法。实际上,Lambda表达式在Java 中只是一个语法糖,底层是基于函数接口实现的,也就是第二段代码展示的写法。

//第一段代码:stream类中map函数的定义
public interface Stream<T> extends BaseStream<T, Stream<T>> {
    <R> Stream<R> map(Function<? super T, ? extends R> mapper);   
    //...省略其他函数.
}

//第二段代码:Stream类中map的数的使用方式
Stream.of("foo", "bar", "hello").map(new function<String, Integer>(){
    @Override
    public Integer apply(String s){
        return s.length ();
    }
});

//第三段代码: 用lambda表达式简化后的写法
Stream.of("foo", "bar", "hello").map(s -> s.length());

Lambda表达式包括3部分:输入、函数体和输出,标准写法如下所示:
(a, b)->{ 语句1; 语句2; ...; return 输出; } //a,b都是输入参数

        实际上,Lambda表达式的写法非常灵活,除上述标准写法以外,还有很多简化写法。例如,如果入参只有一个,那么可以省略“0”,直接写成“a->{….)”;如果没有入参,那么可以直接将输入和箭头都省略,只保留函数体;如果函数体只有一个语句,那么可以将“()”省略:如果函数没有返回值,那么return 语句可以省略。

        如果我们把之前 FPDemo 类示例中的 Lambda 表达式全部替换为函数接口的实现方式,那么如下所示。代码是不是变多了?

0ptional<Integer> result = Stream.of("foo", "bar", "hello")
        .map(s ->s.length())
        .flter(l -> l <= 3)
        .max((o1,o2)->o1-o2);
//将上述Lambda表达式替换为函数接口的实现方式
0ptional<Integer>result2 = Stream.of("foo", "bar", "hello")
        .map(new Function<String,Integer>(){
            @Override
            public Integer apply(String s){
                return s.length();
            }
        })
        .flter(new Predicate<Integer>(){
            @Override
            public boolean test(Integer l){
                return l<=3;
            }
        })
        .max(new Comparator<Integer>(){
            @Override
            public int compare(Integer ol, Integer o2){
                return ol-o2;
            }
        });

        (3)函数接口

        实际上,上面那段代码中的 Function、Predicate和 Comparator 都是函数接口。我们知道,C语言支持函数指针,它可以把函数直接当变量来使用。但是,Java没有函数指针这样的语法,因此,它通过函数接口,将函数包裹在接口中,当做变量来使用。实际上,函数接口就是接口。不过,它有自己特别的地方,那就是要求只包含一个未实现的方法。只有这样,Lambda 表达式才能明确知道匹配的是哪个接口。如果有两个未实现的方法,并且接口入参、返回值都一样,那么Java在翻译Lambda 表达式时,就不知道表达式对应哪个方法。

        为了让读者对函数接口有一个直观的理解,我们把Java提供的Function、Predicate 这两个函数接口的源码列在下面。

@FunctionalInterface
public interface function<T,R>{
    //只有这一个未实现的方法R apply(T t);

    default<V> Function<V, R> compose (Function<? super V, ? extends T> before) {            
        Object.requireNonNull(before);
        return (V v)->apply(before.apply(v));
    }

    default<V> Function<T, V> andThen(Function<? super R, ? extends v> after) {
        Objects.requireNonNull(after);
        return (T t)-> after.apply(apply(t));
    }

    static <T> Function<T,T> identity(){ 
        return t -> t;
    }
}

@FunctionalInterface
public interface Predicate<T>{
    boolean test(T  t);//只有这一个未实现的方法

    default Predicate<T> and(Predicate<? super T> other){
        Objects.requireNonNull(other);
        return (t)->test(t) && other.test(t);
    }

    default Predicate<T> negate(){
        return (t)-> !test(t);
    }

    default Predicate<T> or(Predicate<? super T> other){
        Objects.requireNonNull(other);
        return (t)->test(t)|| other.test(t);
    }

    static <T> Predicate<T>  isEqual(Object targetRef){
        return (null == targetRef)
                ? objects::isNull
                :object -> targetRef.equals(object);
    }
}

5.面向对象编程和函数式编程的对比

        不同的编程范式并不是截然不同的,总有一些相同的编程规则。例如,无论是面向过程编程、面向对象编程,还是函数式编程,它们都有变量、函数的概念,顶层都要有main 函数执行入口,以组装编程单元(类、函数等)。只不过,面向对象编程的编程单元是类或对象,面向过程编程的编程单元是函数,函数式编程的编程单元是无状态函数。

        函数式编程因其编程的特殊性,仅在科学计算、数据处理和统计分析等领域才能更好地发挥它的优势。因此,它并不能完全替代更加通用的面向对象编程范式。但是,作为一种补充它有很大的存在、发展和学习意义。

        面向对象编程侧重代码模块的设计,如类的设计。而面向过程编程和函数式编程侧重具的实现细节,如函数的编写、这也是大部分讲解设计模式的图书喜欢使用面向对象编程语言举例的原因。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/600372.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux】基础命令

常用命令及参数&#xff1a;dir表示文件夹&#xff0c;file表示文件&#xff08;file可表示其他目录下的文件&#xff09; pwd命令&#xff1b;查看当前所属文件夹&#xff08;print working directory&#xff09; ls [选项] dir&#xff1b;查看当前、指定文件夹目录内容&am…

《Fundamentals of Power Electronics》——隔离型CUK转换器、

以下是隔离型CUK转换器的相关知识点&#xff1a; Cuk电路的隔离型版本获得方式不同。基础非隔离型Cuk电路如下图所示。 将上图中电容C1分成两个串联的电容C1a和C1b&#xff0c;得到结果如下图所示。 在两个电容之间插入一个变压器&#xff0c;得到如下图所示电路。 变压器极性…

Quora 首席执行官亚当·德安杰洛 (Adam D’Angelo) 谈论了 AI、聊天机器人平台 Poe,以及 OpenAI 为什么不是竞争对手

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

鸿蒙内核源码分析(进程通讯篇) | 九种进程间通讯方式速揽

进程间为何要通讯 ? 鸿蒙内核默认支持 64个进程和128个任务&#xff0c;由进程池和任务池统一管理.内核设计尽量不去打扰它们&#xff0c;让各自过好各自的日子&#xff0c; 但大家毕竟在一口锅里吃饭&#xff0c; 不可能不与外界联系&#xff0c; 联系就得有渠道&#xff0c…

虚幻引擎笔记

虚幻引擎笔记 一、蓝图类二、创建自定义Character蓝图三、操作映射和轴映射 一、蓝图类 Actor&#xff1a;可以放置在关卡中的物体Pawn&#xff1a;相当于游戏角色&#xff0c;可以通过玩家控制器来控制角色&#xff1a;在Pawn的基础上增加了四处走动的功能。创建游戏角色时使用…

创意无限,批量剪辑技巧:视频剪辑中的画中画技巧大揭秘

在视频剪辑的世界里&#xff0c;创意是无限的&#xff0c;而技巧则是实现这些创意的关键。画中画技巧作为视频剪辑中的一种高级技术&#xff0c;可以带给观众新颖的视觉体验&#xff0c;提升视频的质量和观赏性。本文将深入探讨批量剪辑中的画中画技巧&#xff0c;揭示其背后的…

就业班 第三阶段(redis) 2401--5.7 day2 redis2 哨兵(前提是做好了主从)+redis集群

1、设置密码&#xff08;redis&#xff09; 先在redis.conf里面找到这个 后面写上要设置的密码即可 2、哨兵模式 监控redis集群中master状态的的工具 在做了主从的前提下 主 从1 从2 作用 1)&#xff1a;Master状态检测 2)&#xff1a;如果Master异常&#xff0c;则会进行…

2-5 任务:打印九九表

本次实战的目标是通过编写程序实现打印九九乘法表、字符矩形、字符平行四边形和字符菱形等图形&#xff0c;以及解决百钱买百鸡问题和输出素数等实际问题。在实战过程中&#xff0c;我们将学习并掌握以下知识点。 双重循环的使用&#xff1a;通过双重循环实现九九乘法表的打印&…

告别杂乱桌面,开启纯净视界!DeskCover Pro,Mac用户的桌面神器!

DeskCover Pro for Mac是一款专为macOS设计的桌面图标隐藏软件&#xff0c;其主要功能和特点包括&#xff1a; 桌面图标隐藏&#xff1a;通过单击鼠标或按全局热键&#xff0c;可以快速隐藏桌面上的所有图标&#xff0c;为您提供一个干净整洁的工作环境。窗口聚焦&#xff1a;…

证券基金信创联盟研讨会:YashanDB分享金融核心数据库技术实践

4月26日&#xff0c;由证券基金行业信息技术应用创新联盟主办、WG3稽核风控系统工作组承办、国信证券股份有限公司协办的信创联盟2024年度系列研讨会第三期-稽核风控系统信创实践成功举办。国内头部企业国信证券、申万宏源证券、信达证券、国金证券、广发证券等单位共计300余人…

【数据结构】链表经典OJ题目练习(2)

面试题 02.02. 返回倒数第 k 个节点 - 力扣&#xff08;LeetCode&#xff09; 思路1&#xff1a;先计算出链表的长度&#xff0c;在将链表中的值存在数组中&#xff0c;在返回第k个节点。 思路2&#xff1a;利用快慢指针&#xff0c;先让快指针走k步&#xff0c;在让快慢指针分…

[译]Elasticsearch _source Doc_values And Store Performance

原文地址 https://sease.io/2021/02/field-retrieval-performance-in-elasticsearch.html 在这篇博文中&#xff0c;我想从性能的角度探讨 Elasticsearch 为我们存储字段和查询时检索字段提供了哪些可能性。 事实上&#xff0c;Lucene&#xff08;Elasticsearch 和 Solr 构建的…

详细分析Mybatis与MybatisPlus中分页查询的差异(附Demo)

目录 前言1. Mybatis2. MybatisPlus3. 实战 前言 更多的知识点推荐阅读&#xff1a; 【Java项目】实战CRUD的功能整理&#xff08;持续更新&#xff09;java框架 零基础从入门到精通的学习路线 附开源项目面经等&#xff08;超全&#xff09; 本章节主要以Demo为例&#xff…

122. Kafka问题与解决实践

文章目录 前言顺序问题1. 为什么要保证消息的顺序&#xff1f;2.如何保证消息顺序&#xff1f;3.出现意外4.解决过程 消息积压1. 消息体过大2. 路由规则不合理3. 批量操作引起的连锁反应4. 表过大 主键冲突数据库主从延迟重复消费多环境消费问题后记 前言 假如有家公司是做餐饮…

3行代码,实现一个取色器

前言 今天发现了一个很好玩的 API ——EyeDropper。 EyeDropper API 提供了一种创建拾色器工具的机制。使用该工具,用户可以从屏幕上取样颜色,包括浏览器窗口之外的区域。 这是 MDN 上对它的介绍,可以取包括浏览器窗口之外的区域。我们一起看看是怎么个事 什么是取色器 取…

24年最新AI数字人简单混剪

24年最新AI数字人简单混剪 网盘自动获取 链接&#xff1a;https://pan.baidu.com/s/1lpzKPim76qettahxvxtjaQ?pwd0b8x 提取码&#xff1a;0b8x

【C 数据结构-图】2. 图的存储结构

文章目录 【 1. 图的顺序存储结构 】1.1 基本原理1.2 顺序存储结构的 C 实现 【 2. 图的链式存储结构 】2.1 图的临接表存储结构2.1.1 临接表的 基本原理2.1.2 临接表的 链表节点2.1.3 邻接表 各结构体的C实现2.1.4 临接表 计算顶点的出度和入度邻接表计算 无向图的出度和入度邻…

【Fastadmin】后台角色组权限问题(multi,开关switch,控制器新增方法)

1.列表开关类型的权限 如图&#xff1a; 此类开关请求的方法为multi 开关在点击的时候默认是只允许修改数据库的status字段的&#xff0c;如果我们开关不是status字段&#xff0c;我们需要在服务端对应的控制器中定义protected $multiFields"id,name,swith";&#x…

一个物业管理服务项目的思考——智慧停车场无人值守呼叫系统到电梯五方对讲再到呼叫中心

目录 起源智慧停车场无人值守呼叫系统然后电梯五方对讲系统又然后物业呼叫中心集控E控中心怎么做 起源 小区里新装了智慧停车场系统&#xff0c;马上展现出了科技化、现代化的新形象。一个显著的好处是&#xff1a;停车场的出入口&#xff0c;再也看不到司机和保安争吵的场景了…

四川景源畅信:抖音的运营策略有哪些?

在数字营销的大潮中&#xff0c;抖音以其巨大的用户基础和强大的传播力成为众多品牌和商家的必争之地。那么&#xff0c;抖音的运营策略有哪些呢?这个问题涉及到内容创作、用户互动、数据分析和品牌合作等多个方面。 一、内容创作与优化在抖音&#xff0c;内容是吸引用户的关键…