学习网08月14日:电影她在丈夫的面前被凌辱了_日本拍拍网站✪YvZjyYHj-VSHDYS7123FID-mRtUk13V
在诞生近 70 年之后, 函数式编程(Functional Programming)开始获得越来越多的关注。并且随着时间推移、技术演变,它与前端开发,结合的也越发紧密;前些日子,读了些文章,对函数式编程有了粗浅的了解,并在代码上做了些实践,发现这种思想非常有趣,喜之不已。近日我女神对它突感好奇,于是乎,便结合业界已有之分享,结合自己理解与实践,梳理成文,以便解释。
备注:原文首发于:浅谈关于「函数式编程」的理解 | 宜想悠然亭 ,@2022 年 05 月 30 日。
与非函数式的区别 非函数式的示例代码如下:1
2
3
4
let counter = 0
const increment = () => {
counter++
}
1
2
3
const increment = (counter ) => {
return counter + 1
}
你可能会觉得这个例子太过普通。但,这就是函数式编程的准则:不依赖于外部的数据,而且也不改变外部数据的值,而是返回一个新的值给你 。
再看一段复杂一点,却较为 实用的代码示例 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const handleSearch = (_keywords ) => {
let queryResultArr;
if (!_keywords) {
queryResultArr = []
} else {
queryResultArr = getQueryResult(_keywords)
}
setQueryResultArr(queryResultArr)
setIsShowResults(queryResultArr.length > 0 )
}
const throttle = (fn, wait ) => {
let pre = Date .now();
return function ( ) {
const context = this ;
const args = arguments ;
const now = Date .now();
if (now - pre >= wait) {
fn.apply(context, args)
pre = Date .now()
}
}
}
const requestSearchFunc = throttle(handleSearch, 200 )
const handleInputChange = (event ) => {
const value = event.target.value;
setKeywords(value)
requestSearchFunc(value)
}
可以使用 throttle
来节流函数 ,如:requestSearchFunc;这个技术其实就是 Currying 技术(把一个函数的多个参数分解成多个函数, 然后把函数多层封装起来,每层函数都返回一个函数去接收下一个参数这样,可以简化函数的多个参数)。从这个技术上,你可能体会到函数式编程的理念:把函数当成变量来用,关注于描述问题而不是怎么实现 ,这样可以让代码更易读。
过程式 vs 函数式 过程式编程,主要要采取过程调用,或函数调用的方式来进行流程控制;它主要关注:一步一步地解决问题。这是完全有效的编码方式,但当您希望应用程序扩展时,它存在许多缺点。而函数式编程( Functional Programming)关注的是:描述要做什么,而不是如何做 (describe what to do, rather than how to do it)。
举例来说,对于一个英文名数组,需要将其中短横线命名,转化为大驼峰格式。基于传统过程式编程,可能你不会想太多,直接将想法用代码来表达出来,临时变量、循环语句,用的飞起:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const nameArr = ['nice-jade' , 'rayso-lee' , 'jon-snow' , 'arya-stark' ];
const newArr = [];
for (let i = 0 , len = nameArr.length; i < len ; i++) {
let name = nameArr[i];
let tempNameArr = name.split('-' );
let newNameArr = [];
for (let j = 0 , nameLen = tempNameArr.length; j < nameLen; j++) {
let newFormatName = tempNameArr[j][0 ].toUpperCase() + tempNameArr[j].slice(1 );
newNameArr.push(newFormatName);
}
newArr.push(newNameArr.join(' ' ));
}
console .log(newArr)
这是可以达成目标的答案,它完全面向过程 ;虽然在编码时候,能够将想法淋漓尽致表现出来。但对于阅读的人,十分不够友好。因为这中间夹杂了复杂的逻辑,充斥了大量临时变量、循环、状态变化等等;通常您需要:从头读到尾才知道它具体做了什么 ,而且一旦出问题,很难定位。当然,你也可以将如上代码,拆分成几个函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const nameArr = ['nice-jade' , 'rayso-lee' , 'jon-snow' , 'arya-stark' ]
const capitalize = (str ) => str[0 ].toUpperCase() + str.slice(1 ).toLowerCase()
const convertName = (name ) => {
let tempNameArr = name.split('-' )
let newNameArr = []
for (let j = 0 , nameLen = tempNameArr.length; j < nameLen; j++) {
let newFormatName = capitalize(tempNameArr[j])
newNameArr.push(newFormatName)
}
return newNameArr.join(' ' )
}
const getNewArr = (nameArr ) => {
const newArr = []
for (let i = 0 , len = nameArr.length; i < len; i++) {
let name = nameArr[i]
const newName = convertName(name)
newArr.push(newName)
}
return newArr
}
const newNameArr = getNewArr(nameArr)
console .log(newNameArr)
如此一来,阅读代码时,需要考虑的上下文少了许多,也就更容易。不像第一个示例,如果没有合理的注释说明,你还需要花些时间来理解。而把代码逻辑封装成了函数后,就相当于:给每个相对独立的程序逻辑取了个名字,于是代码成了自解释的 。幸好,在这份代码中,函数间调用,没有依赖共享的变量 ,否则将会更加复杂。但,仍是充斥了临时变量、循环,增加代码量的同时,也加大了理解之难度。
如果基于函数式编程 思想,那会是怎样的代码呢?
1
2
3
4
5
6
7
8
9
const { compose, map, join, split } = require ('ramda' )
const nameArr = ['nice-jade' , 'rayso-lee' , 'jon-snow' , 'arya-stark' ]
const capitalize = (str ) => str[0 ].toUpperCase() + str.slice(1 ).toLowerCase()
const convertName = compose(join(' ' ), map(capitalize), split('-' ))
const newNameArr = nameArr.map((item ) => convertName(item))
console .log(newNameArr)
如上代码(有借助 ramda ——「实用的函数式 JavaScript 工具库」来实现),虽依然把程序的逻辑分成了函数,不过这些函数都是 Functional 的。因为它们有三个症状:
它们之间没有共享的变量。
函数间通过参数和返回值来传递数据。
在函数里没有临时变量。
从这个编程思路,可以清晰看出,函数式编程 的思维过程是完全不同的,它的着眼点是函数 ,而不是过程 ,它强调的是:通过函数的组合、变换去解决问题,而不是通过写什么样的语句去解决问题;当你的代码越来越多的时候,这种函数的拆分和组合,就会产生出更加强大的力量。
什么函数式编程?
函数式编程 ,或称函数程序设计、泛函编程(英语:Functional Programming),是一种编程范式,它将电脑运算视为函数运算,并且避免使用程序状态以及易变对象。比起指令式编程,函数式编程更加强调程序执行的结果,而非执行的过程;倡导利用若干简单的执行单元,让计算结果不断渐进,逐层推导复杂的运算,而不是设计一个复杂的执行过程。
在函数式编程中,函数是头等对象,意思是说一个函数,既可以作为其它函数的输入参数值,也可以从函数中返回值,被修改或者被分配给一个变量。
不仅最古老的函数式语言 Lisp 重获青春,而且新的函数式语言层出不穷,比如 Erlang、clojure、Scala、F#等等。目前最当红的 Python、Ruby、Javascript,对函数式编程的支持都很强,就连老牌的面向对象的 Java、面向过程的 PHP,都忙不迭地加入对匿名函数的支持。越来越多的迹象表明,函数式编程已经不再是学术界的最爱,开始大踏步地在业界投入实用。
也许继”面向对象编程”之后,”函数式编程”会成为下一个编程的主流范式(paradigm)。未来的程序员恐怕或多或少都必须懂一点。—— 函数式编程初探 @阮一峰 (2012 年)
函数,即是一种描述集合和集合之间的转换关系 ,输入通过函数,都会返回有且只有一个 输出值。函数 实际上是一个关系 ,或者说成一种映射,而这种映射关系是可 组合
的,当知道一个函数的输出类型,可以匹配另一个函数的输入,那他们就可以进行组合。如上述代码中提及的 convertName
函数。
在编程世界中,需要处理的逻辑,其实只有“数据”和“关系”,而关系就是 函数
。一旦映射关系(函数)找到了,问题即能迎刃而解;剩下的事情,就是让数据通过这种关系,然后转换成另一个数据而已。
函数式编程的特点 函数是”第一等公民” 所谓 “第一等公民” (First Class),指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。如上文中代码:
1
const convertName = compose(join(' ' ), map(capitalize), split('-' ))
无状态和数据不可变 无状态(Statelessness)和数据不可变(Immutable data),这是函数式编程的核心概念:
数据不可变 :它要求你所有的数据都是不可变的,这意味着如果你想修改一个对象,那你应该创建一个新的对象用来修改,而不是修改已有的对象。
无状态 :主要是强调对于一个函数,不管你何时运行,它都应该像第一次运行一样,给定相同的输入,给出相同的输出,完全不依赖外部状态的变化。
为了实现这个目标,函数式编程提出函数应该具备的特性:没有副作用和纯函数。
没有”副作用” 所谓”副作用”(side effect),指的是函数内部与外部互动(最典型的情况,就是修改全局变量的值),产生运算以外的其他结果。
函数式编程强调没有”副作用”,意味着函数要保持独立,所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。
引用透明 引用透明(Referential transparency),指的是函数的运行不依赖于外部变量或”状态”,只依赖于输入的参数,任何时候只要参数相同,引用函数所得到的返回值总是相同的。
有了前面的第三点和第四点,这点是很显然的。其他类型的语言,函数的返回值往往与系统状态有关,不同的状态之下,返回值是不一样的。这就叫”引用不透明”,很不利于观察和理解程序的行为。
惰性执行 所谓惰性执行(Lazy Evaluation),指的是函数只在需要的时候执行,即:不产生无意义的中间变量。像上面👆的例子,函数式编程跟命令式编程最大的区别就在于:几乎没有中间变量,它从头到尾都在写函数,只有在最后的时候才通过调用 convertName
产生实际的结果。
尾递归优化 迭代在函数式语言中常用递归 来完成,用递归来实现控制流程的机制,是函数式编程的一个非常重要的特点。我想您当然知道 递归
的害处,那就是如果递归很深的话,stack 受不了,并会导致性能大幅度下降。所以,我们使用尾递归优化技术——每次递归时都会重用 stack,这样一来能够提升性能。尾递归的实现,其实是基于编译器的识别和优化的,编译器发现一个函数是尾递归,就会把它实现为与命令式编程中的迭代差不多的汇编码。
函数式编程相关技术
map & reduce :这个技术不用多说了,函数式编程最常见的技术,就是对一个集合做 Map 和 Reduce 操作。这比起过程式的语言来说,在代码上要更容易阅读。(传统过程式的语言需要使用 for/while 循环,然后在各种变量中把数据倒过来倒过去的)这个很像 C++中的 STL 中的 foreach,find_if,count_if 之流的函数的玩法。
pipeline :这个技术的意思是,把函数实例成一个一个的 action,然后,把一组 action 放到一个数组或是列表中,然后把数据传给这个 action list,数据就像一个 pipeline 一样顺序地被各个函数所操作,最终得到我们想要的结果。
compose :它可以接收多个独立的函数作为参数,然后将这些函数进行组合串联,最终返回一个“组合函数”。pipe
与 compose
的共同点是:都返回“组合函数”,区别则是执行的顺序不同,前者是从左向右执行,后者则是从右向左执行。
recursing 递归 :递归最大的好处就简化代码,他可以把一个复杂的问题用很简单的代码描述出来。注意:递归的精髓是描述问题,而这正是函数式编程的精髓。
currying :把一个函数的多个参数分解成多个函数, 然后把函数多层封装起来,每层函数都返回一个函数去接收下一个参数这样,可以简化函数的多个参数。在 C++中,这个很像 STL 中的 bind_1st 或是 bind2nd。
higher order function 高阶函数 :所谓高阶函数就是函数当参数,把传入的函数做一个封装,然后返回这个封装函数。现象上就是函数传进传出,就像面向对象对象满天飞一样。
函数式编程的意义
代码简洁,开发快速 :大量使用函数,减少了代码的重复,因此程序比较短,开发速度较快;
接近自然语言,易于理解 :函数式编程的自由度很高,可以写出很接近自然语言的代码;
更方便的代码管理 :不依赖、也不会改变外界的状态,只要给定输入参数,返回的结果必定相同;
更少的出错概率 :因为每个函数都很小,而且相同输入,永远可以得到相同的输出,测试也很简单;
易于”并发编程” :因为它不修改变量,所以根本不存在”锁”线程的问题;
代码的热升级 :基于没有副作用特点,只要保证接口不变,内部实现是外部无关的。
函数式编程,结语 正如您在函数式编程中看到的,它希望使用小的(理想情况下是纯函数 )函数来解决问题。这种方法也非常具有可 扩展性
,并且函数可以 重用
。
没有更好和更坏的范式。有经验的开发人员,可以看到每种范式的优点,并为给定的问题选择相对更合适的。过程式编程,并不是说你不能使用函数;函数式编程也不会阻止你使用“类”。这些范式,只是以一种随代码增长而有益的方式,来帮助解决问题。
所参考系列文章
猜您可能感兴趣的文章
(看)【kan】(普)【pu】(京)【jing】(的)【de】(表)【biao】(情)【qing】(,)【,】(感)【gan】(觉)【jiao】(是)【shi】(在)【zai】(努)【nu】(力)【li】(憋)【bie】(住)【zhu】(笑)【xiao】(。)【。】
【】 蔡瑁如今一想到诸葛亮,就恨不得割掉对方那根烂舌头,原本占据着绝对的优势,就在这么不到一年的时间里,硬生生被诸葛亮那根三寸不烂之舌,游说各郡,将襄阳给彻底孤立,成了一座名副其实的孤城。《0608新视觉,57歳の熟女セックス,女人说出来混迟早要还的... 。
aodesa✞传言中,一些家庭凭借人才政策落户西安,但工作生活均不在西安,其孩子凭借政策“回流”至西安参加中考,而目的则直指三年后的高考。不少西安家长认为,这挤占了生活、工作、学习均在本地家庭本该享有的教育资源。
作为资深基民,行家对蚂蚁基金并不陌生。↖shijinianqian,zhangchongxiubeizhachuhuanyoushiguanai,weibubeiqiediaolewufenzhisan。raner,zhangchongxiuyiranjianchixiezuo,zhuanxiebingchubanlefanyingkangmeiyuanchaozhanzhengdechangpianxiaoshuo。
( )【 】( )【 】(希)【xi】(普)【pu】(金)【jin】(斯)【si】(的)【de】(对)【dui】(华)【hua】(政)【zheng】(策)【ce】(,)【,】(是)【shi】(新)【xin】(西)【xi】(兰)【lan】(对)【dui】(华)【hua】(态)【tai】(度)【du】(的)【de】(传)【chuan】(承)【cheng】(与)【yu】(延)【yan】(续)【xu】(。)【。】(“)【“】(在)【zai】(记)【ji】(者)【zhe】(会)【hui】(上)【shang】(,)【,】(希)【xi】(普)【pu】(金)【jin】(斯)【si】(曾)【zeng】(表)【biao】(示)【shi】(要)【yao】(继)【ji】(续)【xu】(努)【nu】(力)【li】(经)【jing】(营)【ying】(成)【cheng】(熟)【shu】(的)【de】(中)【zhong】(新)【xin】(关)【guan】(系)【xi】(,)【,】(不)【bu】(让)【rang】(分)【fen】(歧)【qi】(来)【lai】(定)【ding】(义)【yi】(双)【shuang】(边)【bian】(关)【guan】(系)【xi】(,)【,】(此)【ci】(次)【ci】(访)【fang】(华)【hua】(再)【zai】(次)【ci】(释)【shi】(放)【fang】(了)【le】(互)【hu】(相)【xiang】(合)【he】(作)【zuo】(、)【、】(求)【qiu】(同)【tong】(存)【cun】(异)【yi】(的)【de】(信)【xin】(号)【hao】(。)【。】(”)【”】 虽然早知道关中的弩箭厉害,在五年前已经为吕布立下汗马功劳,但时隔五年之后,再次在中原大地展露峥嵘的时候,其表现出来的战斗力,已经可以扭转人数之上的差距了。 lingjuzhongyetiangongjituanyouxiangongsiguanwangqunian11yue14rifabuxinxi,gaigongsigongchengjishuzongyuanzhongbiaoqiqihaershidisanshisizhongxuexinjianfushuzongheloushejixiangmu。gaixiangmuweiyuheilongjiangshengqiqihaershidisanshisizhongxuexiaoyuannei,xiangmushejineirongbaokuoxinjianfushuzonghelou1dong、shebeifang1zuo,yijisujiaopaodao、daolujilumianyinghua、lvhuadengchangqupeitaogongcheng。qizhong,fushuzonghelouzongjianzhumianji6800pingfangmi,dishang5ceng。xiangmujianchenghou,jiangwanshanxuexiaodejichusheshi,quanmiantishengxuexiaodeyingjianhuanjinghejiaoyupinzhi,zhulixuexiaogaozhiliangfazhan。
以抱团的方式发展,这不仅避免了村落间的同质化发展,还能共享资源,丰富游客的游玩体验。这个坐落在仙潭村山顶的露营基地,除了为游客提供住宿之外,还专门建设了一个山顶美术馆,每个月都会邀请一线的画家来办展,为游客和周边村民开办艺术课堂。©( )【 】( )【 】(包)【bao】(括)【kuo】(朝)【chao】(阳)【yang】(商)【shang】(圈)【quan】(、)【、】(埌)【埌】(东)【dong】(片)【pian】(区)【qu】(、)【、】(江)【jiang】(南)【nan】(商)【shang】(圈)【quan】(、)【、】(南)【nan】(湖)【hu】(片)【pian】(区)【qu】(、)【、】(埌)【埌】(西)【xi】(片)【pian】(区)【qu】(。)【。】(具)【ju】(体)【ti】(是)【shi】(:)【:】(厢)【xiang】(竹)【zhu】(大)【da】(道)【dao】(—)【—】(竹)【zhu】(溪)【xi】(大)【da】(道)【dao】(—)【—】(葫)【hu】(芦)【lu】(鼎)【ding】(大)【da】(桥)【qiao】(—)【—】(白)【bai】(沙)【sha】(大)【da】(道)【dao】(—)【—】(亭)【ting】(江)【jiang】(路)【lu】(—)【—】(亭)【ting】(洪)【hong】(路)【lu】(—)【—】(南)【nan】(建)【jian】(路)【lu】(—)【—】(永)【yong】(和)【he】(大)【da】(桥)【qiao】(—)【—】(永)【yong】(和)【he】(路)【lu】(—)【—】(中)【zhong】(华)【hua】(路)【lu】(—)【—】(园)【yuan】(湖)【hu】(北)【bei】(路)【lu】(—)【—】(东)【dong】(葛)【ge】(路)【lu】(—)【—】(厢)【xiang】(竹)【zhu】(大)【da】(道)【dao】(围)【wei】(合)【he】(的)【de】(区)【qu】(域)【yu】(,)【,】(包)【bao】(含)【han】(围)【wei】(合)【he】(道)【dao】(路)【lu】(。)【。】
在白建荣团队的工作室采访时,我不由地想起马克思的那句名言:“在科学上没有平坦的大道,只有不畏劳苦沿着陡峭山路攀登的人,才有希望达到光辉的顶点。”☪ciciqidongjianshedeguojiaqukuailianjishu(haiyangjingji)chuangxinzhongxinweiyuzhejiangshengzhoushanshi。zhoushanshiweishuji、zhoushanqundaoxinqudanggongweishujihezhongweibiaoshi,qukuailianguochuanghaiyangjingjizhongxinjiangchongfenliyongzhoushanqundaoguojiajixinqufazhanhaiyangjingjideyoushi,kaizhanshuzijingjiyuhaiyangjingjishenduronghedehexinkejiyanjiu,quanmiantuidongwoguoshougezizhukekongqukuailianruanyingjianjishutixi“changanlian”zaiyuanyangyouqigongyinglian、jianghailianyun、haiyangyuyeshengchandengguanjianchangjingdeyingyong,peiyanggaoshuipinghaiyangshuzijingjirencai。
masikehaizaihuifuyipianyouguanwangluoshenzhadewenzhangshitidaoleshejiaomeiti。tashuo,chuletuitezhegepingtaiyiwai,qitadedashejiaomeitigongsidouleyugaoyangedeshenzha。 “有熟悉的人作为依靠,
(来源:谭平山)
发布于:淮南市
声明:该文观点仅代表作者本人,搜狐号系信息发布平台,搜狐仅提供信息存储空间服务。