当前位置: 首页 > 后端技术 > Python

FPParadigmTour

时间:2023-03-26 02:10:14 Python

OO通过封装移动部件使代码易于理解,但FP通过最小化移动部件使代码易于理解。-MichaelFeathers什么是FP(FunctionalProgramming),而且似乎没有一个被大家普遍认可的定义,我今天不打算在这里讨论这个问题。这篇文章的目的是带大家感受一下函数式编程,而不是函数式编程教程。一开始,我们打算从一个简单的例子带你进入函数式编程的世界。我打算从三种不同的语言来介绍它。如果读者不熟悉某种语言,或者只想阅读他们熟悉的语言,可以完全跳过。如果你对语言不感兴趣,我会尽量把每一种语言都当作一个独立的单元来对待。因为破冰只是给大家看体验,所以我们选择的题目都比较简单。该题输入了一系列文件,要求我们统计每个文件的行数。说明为了简化问题,这里我们只统计每个文件中换行符\n的个数,假设最后一行也有换行符(当然也可以直接加1得到最终的真实行数).命令式如果使用传统的命令式编程方式,大概需要进行如下分析:0.定义一个文件行数统计表,用于保存每个文件的行数;1.循环打开文件;2.定义一个计数器变量count;3.读取文件字符,判断是否为回车符\n,如果是则加1计数;4、读取当前文件后,存储当前文件的行数;5.循环结束后,返回统计结果。上面的思路很清晰,符合我们平时的开发流程。接下来,我们深入到每一种语言,看看如何从功能的角度来解决。C++表达式本例中,我们使用vector表示输入文件集,vector表示对应的文件行数。命令式这里我们先用命令式的方式实现这个需求,让大家更直观的感受它和函数式思维的区别std::vectorcount_lines_in_files(conststd::vector&files){std::vector行;字符c=0;for(constauto&file:files){intcount=0;std::ifstream在{文件}中;while(in.get(c)){if(c=='\n')count++;}lines.push_back(计数);}returnlines;}Functional我们定义了一个辅助函数open_file来将文件转换为文件流。std::ifstreamopen_file(std::stringfile){returnstd::ifstream{file};}然后统计文件流中换行符\n的个数intcount_lines(std::ifstreamin){returnstd::count(std::istreambuf_iterator(in),std::istreambuf_iterator(),'\n');}最后我们将上面的转换函数open_file和统计函数count_lines分别映射到输入文件列表中,在C++语言中,映射操作是std::transform,在python和Java中是map函数,这类函数在函数式编程中称为高阶函数,它们可以接受函数作为形参,并对待作为一等公民发挥作用。std::vectorcount_lines_in_files(conststd::vector&files){std::vector行(files.size());std::vectorfilestreams(files.size());std::transform(std::begin(files),std::end(files),std::begin(filestreams),open_file);std::transform(std::begin(filestreams),std::end(filestreams),std::begin(lines),count_lines);returnlines;}在上面的解决方案中,我们不关心如何打开文件以及如何进行统计。在上面的程序中,我们只是告诉计算机我们要计算给定流中的换行符\n。这里我们封装了统计换行符的动作count_lines,只是为了表示我们不关心count_lines这个动作。这次我们计算换行符。\n,下次你可以统计任何字符或单词,一切,只要它符合我们的接口契约。函数式编程的主要思想——用抽象来表明我们的目的,而不是如何去做,只是指定将输入转换成想要的输出。对于上面的程序,我们可以实现范围和范围的转换(这个是C++20引入的),函数的用途会更加明确。std::vectorcount_lines_in_files(conststd::vector&files){返回文件|转换(打开文件)|transform(count_lines);}这里range使用pipeline|operator表示通过conversion一个集合,有兴趣的读者可以研究一下。Java表达式在本例中,我们可以使用List来表示输入的文件集合,然后构造一个List来表示对应的文件行数。命令式命令式的答案这里就不展示了,有兴趣的读者可以自己尝试一下。函数式风格下面看看java是如何通过函数式思维来解决这个问题的。首先,我们定义一个统计函数countLine来统计文件中换行符\n的个数。staticlongcountLine(Filefile){longcount=0;try(Streamlines=Files.lines(Paths.get(file.toURI()),Charset.defaultCharset())){count=lines.地图(线->Arrays.stream(线。拆分(“\n”)))。计数();}catch(IOExceptione){}returncount;}借助这个辅助函数,我们利用Java8的流特性结合映射操作map,将输入文件列表映射到统计函数countLine,最后使用收集器进行终端操作。publicstaticListcountFilesLine(Listfiles){returnfiles.stream().map(LinesCounter::countLine).collect(toList());}上面的LinesCounter::countLine调用了一个方法参考,对流或方法参考不熟悉的读者可以参考RichardWarburton的Java8FunctionalProgramming一书。Python表达式由于python语言本身的特点,C++和Java对类型的要求并不严格。在这个例子中,我们可以使用python内置的列表数据类型来表示输入文件的列表和输出文件的行数。功能示例对于python语言来说比较简单。我们特意用下面的方法来回答这个问题,但我们可能不是实时的。defopen_file(file):withopen(file,'r')asf:returnf.read()defcount_line():returnlambdafile:open_file(file).count('\n')defcount_lines_in_files(文件):returnlist(map(count_line(),files))当然我们可以使用python的readlines返回文件中所有行的列表,然后通过映射操作计算列表的长度。defread_lines(file):withopen(file)asfin:returnfin.readlines()defcount_lines_in_files(files):returnlist(map(len,map(read_lines,files)))但是,上述方法有一个缺点,因为readlines()方法读取的是整个文件的所有行,并保存为一个列表,当文件过大时会占用过多的内存。备注虽然我在这里介绍了FP的一些优点,但是并不代表命令式没有用。相反,我是OO的坚定支持者和FP的粉丝。我个人更喜欢多范式组合编程。看完这个例子,有没有勾起你对函数式编程的兴趣呢?既然如此,那就行动吧……