函数式编程小思考2 笔记

函数式编程与面向对象编程的比较

什么是函数式编程?
是一种编程范型,它将电脑运算视为数学上的函数计算,并且避免使用程序状态以及易变对象。
可以接受函数当作输入(引数)和输出(传出值)。

什么是面向对象编程?
是种具有对象概念的程序编程范型,同时也是一种程序开发的方法。它可能包含数据、属性、代码与方法。对象则指的是类的实例。它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性,对象里的程序可以访问及经常修改对象相关连的数据。在面向对象程序编程里,计算机程序会被设计成彼此相关的对象。

函数式编程的基本单位是函数,
面向对象编程的基本单位是对象.
面向对象的思路,更加符合人类日常生活的概念.

面向对象和面向过程的区别
面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。

面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。

函数式编程介绍

编程范式
编程语言主要有三种类型[3]:

  • 命令式编程(Imperative Programming): 专注于”如何去做”,这样不管”做什么”,都会按照你的命令去做。解决某一问题的具体算法实现。
  • 函数式编程(Functional Programming):把运算过程尽量写成一系列嵌套的函数调用。
  • 逻辑式编程(Logical Programming):它设定答案须符合的规则来解决问题,而非设定步骤来解决问题。过程是事实+规则=结果。

命令式编程 关注的是 步骤
有变量, 有赋值, 有控制(if,else), 有状态

函数式编程 关心数据之间的映射关系,
全都是计算,都是表达式求值.
一个函数的值仅决定于函数参数的值,不依赖其他状态。
不依赖外部状态

函数与方法
方法就是命令式编程中的函数,而函数则是函数式编程中的函数。

函数式语言中的条件语句,循环语句等也不是命令式编程语言中的控制语句,而是一种表达式。

  • “表达式”(expression)是一个单纯的运算过程,总是有返回值;
  • “语句”(statement)是执行某种操作(更多的是逻辑语句。),没有返回值。

严格意义上的函数式编程意味着不使用可变的变量,赋值,循环和其他命令式控制结构进行编程。

状态
命令式编程 :有了状态,我们的程序才能不断往前推进,一步步向目标靠拢的。(根据状态进行控制)
函数式编程强调无状态,不是不保存状态,而是强调将状态锁定在函数的内部,不依赖于外部的任何状态。更准确一点,它是通过函数创建新的参数或返回值来保存程序的状态的。
状态完全存在栈上。

函数即不依赖外部的状态也不修改外部的状态,函数调用的结果不依赖调用的时间和位置,这样写的代码容易进行推理,不容易出错。

到这里,我先试着用我所学过的js 来去理解一下

1.有语句, 和表达式
语句是用来控制, 有逻辑语句, 没有返回值
表达式是 一种运算过程.

let num 是一个声明式
num = 9 是一个 表达式(赋值运算) 是有返回值的 返回9
num > 9 也是个表达式(比较运算) 是有返回值的 返回 true
而if () {} 这个就是逻辑语句, 用来进行对状态(也就是num)进行判断,进行控制.
console.log() 这个应该是个语句吧, 因为怎么看他都只是一个动作, 而不是计算一个返回值.
            let num = 9;
            if (num > 9) {
                console.log("bigger than 9");
            }

函数式编程的意思就是, 每个句子,都应该是表达式, 都应该是有返回值的.

num > 9 ? "bigger than 9" : null
三目运算符, 虽然用的概念是if else, 但这是一个表达式, 是有返回值的.

函数与方法


            function num (num) {/ 应该说这个函数是一个方法
                console.log(++num);
            }
            function num (num) {/ 这个函数就是个函数, 因为他是强调有计算,有返回值?
              return ++num;
            }

不依赖外部变量

依赖外部变量的
            let obj = {num : 1};
            
            function num (obj) {
              obj.num++;
            }
            num(obj); / 外部变量 obj 就变了. 并且这是没有返回值的.

不依赖外部变量. 
关键是不会改变外部变量.
            let obj = {num : 1};
            function num (obj) {
              let newObj = {// 如果num是个引用值, 我们应该用深度克隆
                num : ++obj.num
              };
              
              return newObj;
            }
            
            let newObj = num(obj);

其实, 稍微想一下, 我们就会有一个疑问.
如果按照函数式编程,
那么就不应该出现 两个变量指向同一个引用地址吧?

            let obj = {name : "mike"};
            
            let obj1 = obj;
这个赋值是否违背了函数式编程?
因为这样就出问题了
            let obj = {num : 1};
            
            function returnNewObj (obj) {
              let newObj = obj;
                return obj
            }
            
            let newObj = returnNewObj(obj);
            
            newObj.num = newObj.num + 1;
/这样的话 newObj 是会影响到 obj的

或者严格来讲, 也许函数编程应该是让所有语句都被包裹在函数当中?

把上面的翻译一下
            function obj () {
              return {num : 1};
            }
            
            function returnNewObj (obj) {
                return obj();
            }
            
            function newObj (returnNewObj,obj) {
                return returnNewObj(obj)
            }
            
            function addOne (newObj,returnNewObj,obj) {
                return newObj(returnNewObj,obj).num + 1;
            }
            
            console.log(addOne(newObj,returnNewObj,obj));/2

哇塞,看段代码有点发憷啊.

难道说, 用函数式编程, 就根本不需要声明一个变量来接收什么值了?
唯一声明的变量类型就是 函数?
有点诡异啊.
如果真是这样, 那也能稍微理解了下面这句话,
每个函数的参数确定, 则输出就是确定的.
每个函数的执行都会返回一个值.

我怎么进行复用?比如 我就想复用 addOne(newObj,returnNewObj,obj)返回的值,
进行复用要怎么做?每次都写这么一段? 也不是不可以..

不过这显然不合理,因为如果没有变量赋值, 那我想要在前面的计算结果上进行计算,
就必然需要把这条链上需要的所有的函数都当做参数传进去吧?

如果函数式编程当中允许 let a = b 存在,
那也就是说, 不需要所有的表达式都放进函数当中.
这个应该是合理的.

不依赖外部参数的另一个特征?

按照我的理解, 当然也不是确定,
像下面这段代码中,
函数add 内部是直接访问了父级作用域链上的变量num;
而这种行为,在函数式编程当中应该是不允许的.
(这种行为是否称为域逻辑,回头百度一下)
            let num = 1;
            function add () {
                let own = num;
                return own;
            }

函数式编程要求, 参数应该只能通过参数入口传进来.
function add (num) {
  let own = num;
  return own;
}

其实我个人也觉得尽量应该这么做,
因为这样我们就能看到数据的流向是比较明确的.
依赖关系是比较清晰的.
随意引用父级作用域链上的变量, 出错误时, 可能不明白这个数据是从何而来.

其实这也是一种启示.
我们都知道, 在子级函数当中,能够访问父级作用域链上的变量.
也就是说, 父级作用域链上的变量,
对子级函数而言, 读写操作权限是全部开放的.

            let num = 1;
            
            
            (function () {
                num++;
                (function () {
                    num++;
                })()
            })();
            
            (function () {
                num++;
            })();

上面的三个函数均可以对num变量进行读写操作.
所以严格来讲, num 这种暴露方式 其实是很危险的?

3什么是函数式编程思维?

函数式编程关心数据的映射,命令式编程关心解决问题的步骤

首先判断节点是否为空;然后翻转左树;然后翻转右树;最后左右互换。
这就是命令式编程——你要做什么事情,你得把达到目的的步骤详细的描述出来,然后交给机器去运行。

这也正是命令式编程的理论模型——图灵机的特点。一条写满数据的纸带,一条根据纸带内容运动的机器,机器每动一步都需要纸带上写着如何达到。

...这段代码体现的思维,就是旧树到新树的映射——对一颗二叉树而言,它的镜像树就是左右节点递归镜像的树。
... 通过描述一个 旧树->新树 的映射,而不是描述“从旧树得到新树应该怎样做”来达到目的。
...函数式的代码是“对映射的描述”,它不仅可以描述二叉树这样的数据结构之间的对应关系,任何能在计算机中体现的东西之间的对应关系都可以描述——比如函数和函数之间的映射(比如 functor);比如外部操作到 GUI 之间的映射(就是现在前端热炒的所谓 FRP)。它的抽象程度可以很高,这就意味着函数式的代码可以更方便的复用。

同时,将代码写成这种样子可以方便用数学的方法进行研究(这就是为什么可以扯上“范畴上的”这种数学上的高深概念)

至于什么科里化、什么数据不可变,都只是外延体现而已。

这篇文章, 有点大神的味道,你闻到没?
当然他用的是python我不懂,
不过这个文章, 给我的印象是,

命令式编程 ------ 关注的是步骤, 也就是首先干什么,然后干什么
如果状态是什么状态时, 就干什么,
如果是另一个状态,另一种情况时 , 我就干另一件什么.

其实我在敲代码之前,就是这么考虑问题的.
因为敲代码,写项目, 可以看成是个工程项目,
工程项目,自然是要规划步骤,
要么是根据需求逆向反推,
要么是根据初始条件逼近需求.
总会在脑子想, 先干什么, 然后干什么.

函数式编程 ----- 关注的是 关系, 是对关系的描述.
所以很明显这跟数学更加相关?
因为正确清晰的描述一个关系,
在数学上更容易做到,
然后把数学上的关系, 转换成代码.

编程语言的主要类型,声明式编程,命令式编程()和函数式编程的区别

声明式编程:专注于”做什么”而不是”如何去做”。在更高层面写代码,更关心的是目标,而不是底层算法实现的过程。
ex: css, 正则表达式,sql 语句,html, xml…
命令式编程(过程式编程) : 专注于”如何去做”,这样不管”做什么”,都会按照你的命令去做。解决某一问题的具体算法实现。
函数式编程:把运算过程尽量写成一系列嵌套的函数调用。
函数式编程强调没有”副作用”,意味着函数要保持独立,所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。
所谓”副作用”(side effect),指的是函数内部与外部互动(最典型的情况,就是修改全局变量的值),产生运算以外的其他结果。(详细了解函数式编程请看阮一峰的函数编程初探文章)

声明式编程和命令式编程的比较

命令式编程:命令“机器”如何去做事情(how),这样不管你想要的是什么(what),它都会按照你的命令实现。
声明式编程:告诉“机器”你想要的是什么(what),让机器想出如何去做(how)。

如果我们花时间去学习(或发现)声明式的可以归纳抽离的部分,它们能为我们的编程带来巨大的便捷。首先,我可以少写代码,这就是通往成功的捷径。而且它们能让我们站在更高的层面是思考,站在云端思考我们想要的是什么,而不是站在泥里思考事情该如何去做。

这篇文章似乎和函数式编程没什么关系,讲的主要是 声明式编程.

按照这篇文章,我个人的理解是这样的.

            let arr = [1,2,3];
            
            for(let i = 0; i < arr.length; i++) {
              console.log(arr[i]);
            }
            
用步骤来描述的话, 应该是, 
取出arr[0], 输出arr[0]
取出arr[1], 输出arr[1]
取出arr[2], 输出arr[2]

用概念抽象来说的话,应该是
我们说 遍历输出arr的所有项.
存在两个概念, 一个是 遍历, 一个是 输出

所以从某种角度上来讲, for语句本身 就是一种循环概念的体现.

how 的理解是, 一系列的步骤,
怎么理解 what呢?
实际上 what 首先是一种概念的实现. 其次才是一个功能的实现.
如果没有循环的概念, 就不会出现for
如果没有遍历的概念, 就不会出现 each?

Array.prototype.each = function (fn) {
  for(let i = 0; i < this.length; i++) {
    fn(this[i],i,this);
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,029评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,238评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,576评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,214评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,324评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,392评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,416评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,196评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,631评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,919评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,090评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,767评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,410评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,090评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,328评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,952评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,979评论 2 351

推荐阅读更多精彩内容