函数式编程第一步——流程控制

  失落迷茫了好一段日子。终于我用接触2个月的技术Nodejs成功的混到一份工作。严格来说只学习了3天(白天睡觉,晚上通宵学习),后面的时间都是在配置环境。总的来说,函数式编程是有应用的市场的,而且学习门槛也不是太高。就算从来没听说过函数式编程的人也会知道javascript,也会使用jquery。虽然很多是把它当作过程式的来用,来看待。这也是在于它的语法看起来太像C,太像过程式的语言。

  之前一直想写一些关于函数编程文章来记录我学习的历程。之前写了一篇使用F#的,不过大家好像对F#比较排斥。以后我从工作出发写nodejs的吧。

  好了。废话不多说我们先从一个具体的项目来分析函数式编程吧。

  用webstorm新建一个express项目,这是nodejs下用来做web服务器的库。会生成类似下面这个结构的文件。

  • /bin/www : 项目的启动文件,配置了监听的端口,当然程序入口还是app.js

  • /node_modules/ :
    通过npm包管理中间件都在这,包括session,模板,日志等中间件,你自己安装的中间件也在这

  • /public/  :
    暴露的文件夹,从名字就可以看出,图面前端js脚本和css会在这里

  • /routes/ : 路由,相当于控制器

  • /views/ : 模板文件

  • /app.js : 约定俗成的项目入口

  • /package.json : 配置你项目依赖的包,使用npm命令 npm install -d
    会自动安装里面记录的中间件,非常方便。由于nodejs的中间件不完全是脚本组成的,也会包含C写的编译文件,各环境下不尽相同,所以通过npm,本地下载编译是非常重要的

  总的来说文件结构只是约定俗成,或是按人们习惯来用的。不像java、C之类的会有main函数作为入口。任何文件都能当作启动入口。nodejs也不仅限于开发web服务器,加上各种奇葩的中间件的运用,会让项目变成各种形态。这是一个自由度非常高的开发平台。

  我们先写一个简单的demo。由于js的语法太过纠结,我们使用另外一种语言coffeescript,他是一个nodejs的库。能自己运行在nodejs上,也能编译成js文件。这里我们只是用做语法糖,仍然编译成js文件。我会贴出两种代码来适应不同的需要。

coffeescript


fna = ->
console.log("I am ‘a‘")

fnb = ->
console.log "I am ‘b‘"

fna()
fnb()

javscript


// Generated by CoffeeScript 1.7.1
(function() {
var fna, fnb;

fna = function() {
return console.log("I am ‘a‘");
};

fnb = function() {
return console.log("I am ‘b‘");
};

fna();

fnb();

}).call(this);

//# sourceMappingURL=test.map

  这里我编写了两个函数,并依次调用它们。coffeescript会严格申明变量和闭包,不会让其污染全局变量。代码精简不少,看起来也更像是函数式编程了。输入结果显而易见。

console.log

i am ‘a‘
i am ‘b‘

  nodejs是异步执行的。如果这是两个有关联的函数呢?

coffeescript


fna = ->
console.log("这是母鸡")

fnb = ->
console.log "母鸡下蛋"

fna()
fnb()

javascript


// Generated by CoffeeScript 1.7.1
(function() {
var fna, fnb;

fna = function() {
return console.log("这是母鸡");
};

fnb = function() {
return console.log("母鸡下蛋");
};

fna();

fnb();

}).call(this);

//# sourceMappingURL=test.map

  单从结果来看,好像没有什么问题。

console.log

这是母鸡
母鸡下蛋

  在实际项目中,我们并不知道两个函数内部到底干了什么,就像蝴蝶效应,任何改动都可能让结果发生变动。

coffeescript


fna = ->
setTimeout ->
console.log("这是母鸡")
, 100

fnb = ->
console.log "母鸡下蛋"

fna()
fnb()

javascript


// Generated by CoffeeScript 1.7.1
(function() {
var fna, fnb;

fna = function() {
return setTimeout(function() {
return console.log("这是母鸡");
}, 100);
};

fnb = function() {
return console.log("母鸡下蛋");
};

fna();

fnb();

}).call(this);

//# sourceMappingURL=test.map

console.log

母鸡下蛋
这是母鸡

  现在就不是我们想要的结果了。其实这种异步方式也很好理解,它只管函数调用,而不管函数结果。在同步编程中,前一步操作会阻塞后一步操作,母鸡下蛋的操作会等着这只母鸡出结果。而异步编程中,不会阻塞后面的任务进行,就像指挥官给手下发派任务,手下都会去执行各自的任务,但什么时候完成任务就不好说了。这样做的好处就是在执行耗时任务的时候,其他的任务也能继续执行,或者同时执行多个耗时任务。但是有利有弊,在流程控制上会比较纠结。常规做法是用回调函数,就像有人说过,世上本来没有回调,用的人多了也就有了回调函数。

coffeescript


fna = (next) ->
setTimeout ->
console.log("这是母鸡")
next()
, 1000

fnb = ->
console.log "母鸡下蛋"

fna ->
fnb()

javascript


// Generated by CoffeeScript 1.7.1
(function() {
var fna, fnb;

fna = function(next) {
return setTimeout(function() {
console.log("这是母鸡");
return next();
}, 1000);
};

fnb = function() {
return console.log("母鸡下蛋");
};

fna(function() {
return fnb();
});

}).call(this);

//# sourceMappingURL=test.map

conslole.log

这是母鸡
母鸡下蛋

  这中方法虽然解决了关联函数的流程控制问题,但是也有新的问题。逻辑复杂的时候,回调嵌套就会越来越深。

coffeescript


fna = (next) ->
setTimeout ->
console.log("这是母鸡")
next()
, 1000

fnb = (next) ->
setTimeout ->
console.log "母鸡下蛋"
next()
, 100

fnc = ->
console.log "蛋孵出了鸡"

fna ->
fnb ->
fnc()

javascript


// Generated by CoffeeScript 1.7.1
(function() {
var fna, fnb, fnc;

fna = function(next) {
return setTimeout(function() {
console.log("这是母鸡");
return next();
}, 1000);
};

fnb = function(next) {
return setTimeout(function() {
console.log("母鸡下蛋");
return next();
}, 100);
};

fnc = function() {
return console.log("蛋孵出了鸡");
};

fna(function() {
return fnb(function() {
return fnc();
});
});

}).call(this);

//# sourceMappingURL=test.map

console.log

这是母鸡
母鸡下蛋
蛋孵出了鸡

  幸好有中间件解决这个问题。async
中间件有各种流程控制方法。其中series就能很优美的实现这个逻辑。你所要做的就是每个函数里加上一个回调next执行下一步操作,第一个参数是err,第二个参数能追加一个结果,在async最后的回调中返回出来。

coffeescript


async = require "async"
fna = (next) ->
setTimeout ->
console.log "这是母鸡"
next(null, 1)
, 1000

fnb = (next) ->
setTimeout ->
console.log "母鸡下蛋"
next(null, 2)
, 2000

fnc = (next) ->
setTimeout ->
console.log "蛋孵出了鸡"
next(null, 3)
, 100

async.series [
fna
fnb
fnc
]
, (err, results) ->
console.log results

javascript


// Generated by CoffeeScript 1.7.1
(function() {
var async, fna, fnb, fnc;

async = require("async");

fna = function(next) {
return setTimeout(function() {
console.log("这是母鸡");
return next(null, 1);
}, 1000);
};

fnb = function(next) {
return setTimeout(function() {
console.log("母鸡下蛋");
return next(null, 2);
}, 2000);
};

fnc = function(next) {
return setTimeout(function() {
console.log("蛋孵出了鸡");
return next(null, 3);
}, 100);
};

async.series([fna, fnb, fnc], function(err, results) {
return console.log(results);
});

}).call(this);

//# sourceMappingURL=test.map

console.log

这是母鸡
母鸡下蛋
蛋孵出了鸡
[ 1, 2, 3 ]


更好的封装,应该是这个样子。

coffeescript


async = require "async"
fna = (next) ->
setTimeout ->
console.log "这是母鸡"
next()
, 1000

fnb = (next) ->
setTimeout ->
console.log "母鸡下蛋"
next()
, 2000

fnc = (next) ->
setTimeout ->
console.log "蛋孵出了鸡"
next()
, 100

async.series [
(next) ->
fna ->
next null, 1
(next) ->
fnb ->
next null, 2
(next) ->
fnc ->
next null, 3
]
, (err, results) ->
console.log results

javascript


// Generated by CoffeeScript 1.7.1
(function() {
var async, fna, fnb, fnc;

async = require("async");

fna = function(next) {
return setTimeout(function() {
console.log("这是母鸡");
return next();
}, 1000);
};

fnb = function(next) {
return setTimeout(function() {
console.log("母鸡下蛋");
return next();
}, 2000);
};

fnc = function(next) {
return setTimeout(function() {
console.log("蛋孵出了鸡");
return next();
}, 100);
};

async.series([
function(next) {
return fna(function() {
return next(null, 1);
});
}, function(next) {
return fnb(function() {
return next(null, 2);
});
}, function(next) {
return fnc(function() {
return next(null, 3);
});
}
], function(err, results) {
return console.log(results);
});

}).call(this);

//# sourceMappingURL=test.map

到这里coffeescript还可以一战,js你已经完全看不懂了对不对?

  今天就写到这里了,我接触到的范围也不广,以后大家有什么关于函数式编程的问题可以告知,大家一起解决。

函数式编程第一步——流程控制,布布扣,bubuko.com

时间: 2024-12-25 17:22:06

函数式编程第一步——流程控制的相关文章

Python黑帽编程2.4 流程控制

Python黑帽编程2.4  流程控制 本节要介绍的是Python编程中和流程控制有关的关键字和相关内容. 2.4.1 if …..else 先上一段代码: #!/usr/bin/python # -*- coding: UTF-8 -*- x=int(input('请输入一个整数:')) if x==0: print '%d ==0' % x elif x<0: print '%d <0' % x else: print '%d >0' % x 这段代码使用if,elif和else三个

编程第一步

3.2 编程第一步当然,除了将两数简单相加,python可以完成很多复杂的工作.比如我们可以写出fibonacci序列. >>> # Fibonacci series: ... # the sum of two elements defines the next ... a, b = 0, 1 >>> while b < 10: ... print b ... a, b = b, a+b ... 1 1 2 3 5 8 这个例子包含了很多新的特性. # 第一行包含

python012 Python3 编程第一步

Python3 编程第一步在前面的教程中我们已经学习了一些 Python3 的基本语法知识,下面我们尝试来写一个斐波纳契数列.实例如下: #!/usr/bin/python3 # Fibonacci series: 斐波纳契数列 # 两个元素的总和确定了下一个数 a, b = 0, 1 while b < 10: print(b) a, b = b, a+b 执行以上程序,输出结果为: 1 1 2 3 5 8 这个例子介绍了几个新特征.第一行包含了一个复合赋值:变量 a 和 b 同时得到新值 0

Python3 编程第一步

这个例子介绍了几个新特征. 1.  a, b = b, a+b 第一行包含了一个复合赋值:变量 a 和 b 同时得到新值 0 和 1.最后一行再次使用了同样的方法,可以看到,右边的表达式会在赋值变动之前执行.右边表达式的执行顺序是从左往右的 2.end 关键字 关键字end可以用于将结果输出到同一行,或者在输出的末尾添加不同的字符. 3.Python3 条件控制 Python条件语句是通过一条或多条语句的执行结果(True或者False)来决定执行的代码块. 可以通过下图来简单了解条件语句的执行

C#编程(3_流程控制)

一个简单的Bool循环: namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Console.WriteLine("Please Enter an integer:"); int myInt=Convert.ToInt32(Console.ReadLine()); bool isLessThan10 = myInt < 10; bool isBetweenThan0An

python之流程控制与运算符

第一:流程控制 一:if条件语句 计算机之所以能做很多自动化的任务,因为它可以自己做条件判断. 单分支语句: 单分支,单个条件 age = 20 if age >= 18: print('your age is ',age) print('if 判断语句是true') 结果: your age is 20 if 判断语句是true 单分支,多个条件 age = 20 if age >= 18 and age < 19: print('your age is ',age) print('i

Java全栈第一篇-流程控制语句

第一章 流程控制 1.1.为什么要实现流程控制?意义在哪里? 基本介绍:语句的执行顺序对程序执行的结果是有影响的,只有清楚语句的执行流程,我们才能正确的预估程序的运行结果,所以我们要采用控制语句来实现我们对语句执行顺序的把控. 1.2.顺序结构 基本介绍:顺序结构,也是最简单一种结构形式,即语句从上到下依次执行. 程序实例: public class Squence { // 创建一个顺序结构的类 public static void main(String[] args) {// 打印输出一段

shell脚本编程——流程控制

shell脚本编程--流程控制 目   录 一. if 二. case 三. for 四. while 五. until 六. 综合应用 一.if 1.语法 (1)单分支 if  判断条件:then fi (2)双分支 if 判断条件; then 条件为真的分支代码 else 条件为假的分支代码 fi (3)多分支 if 判断条件1; then 条件为真的分支代码 elif 判断条件2; then 条件为真的分支代码 elif 判断条件3; then 条件为真的分支代码 else 以上条件都为假

Scala函数式编程设计原理 第一课 编程范式(Programming Paradigms)

我使用Scala有一两年的时间了,这门语言仿佛有一种魔力,让人用过就不想放手.Scala给我的整个程序生涯带来了非常深刻的影响,让我学会了函数式编程,让我知道了世界上居然还有这么一种优雅.高效.强大的语言. Scala在国外已经非常流行,但是不知为何,在国内总是不温不火,在此,我特别想为Scala这门语言在国内的发展做一些事情.不才不敢谈Scala的编程经验,因为要成为Scala大神还有很长的路要走,只好翻译一份Scala视频教程以飨读者,大家发现有误的地方,请多多批评指教. 这个视频的作者是S