资讯专栏INFORMATION COLUMN

JavaScript函数式编程之pointfree与声明式编程

zorpan / 796人阅读

摘要:函数式编程中的的意思就是无参或无值,是一种编程范式,也作,就是无参编程的意思了。的声明式代码是函数式编程应该有的样子。

函数式编程中的pointfree的意思就是“无参”或“无值”,pointfree style是一种编程范式,也作tacit programming,就是“无参编程”的意思了。什么是“无参编程”?

// 这就是有参的,因为有word
var snakeCase = word => word.toLowerCase().replace(/s+/ig, "_");

// 这是pointfree
var snakeCase = compose(replace(/s+/ig, "_"), toLowerCase);

从另一个角度看,有参的函数的目的是得到一个数据,而pointfree的函数的目的是得到另一个函数。
所以,如下的方程,虽然也有参,也可以认为是pointfree的。

const titlesForYear = year =>
  pipe(
    filter(publishedInYear(year)),
    map(book => book.title)
  )

那这pointfree有什么用?
它可以让我们把注意力集中在函数上,参数命名的麻烦肯定是省了,代码也更简洁优雅。
需要注意的是,一个pointfree的函数可能是由众多非pointfree的函数组成的,也就是说底层的基础函数大都是有参的,pointfree体现在用基础函数组合而成的高级函数上。如果我们使用函数式编程的工具,如ramda,这些基础函数大都已经被写好了,这样我们去写pointfree的代码就很容易了。

什么是声明式编程?它区别于命令式编程

// 命令式
var words = [];
for (i = 0; i < otherWords.length; i++) {
  words.push(otherWords[i].word);
}

// 声明式
var words = otherWords.map(function(ele){ return ele.word; });

容易看出,命令式的代码,我们不但要去遍历,还要关注如何遍历。而声明式的就容易很多,可以节省我们的注意力,代码也更加简洁。

其他的命令式的写法有:使用ifelse进行的条件判断,使用算数运算符进行的算数运算,使用比较运算符进行的比较运算和使用逻辑运算符进行的逻辑运算。

至于那些说“虽然如此,但使用命令式循环速度要快很多”的人,我建议你们先去学学 JIT 优化代码的相关知识。这里有一个非常棒的视频,可能会对你有帮助。

需要注意的是,要实现这种声明式的编程,首先我们要有这个map方法,这一点与pointfree相同,都是需要我们先对常用的操作做一次封装,而这些常用的操作本身还是命令式的。

pointfree的声明式代码是函数式编程应该有的样子。

最后用一个来自Scott Sauyet的文章《Favoring Curry》中的例子,使用的函数式工具是ramda。下面的代码不需要一句一句的看,大概体会一下就可以了。

一组JSON数据

var data = {
    result: "SUCCESS",
    interfaceVersion: "1.0.3",
    requested: "10/17/2013 15:31:20",
    lastUpdated: "10/16/2013 10:52:39",
    tasks: [
        {id: 104, complete: false,            priority: "high",
                  dueDate: "2013-11-29",      username: "Scott",
                  title: "Do something",      created: "9/22/2013"},
        {id: 105, complete: false,            priority: "medium",
                  dueDate: "2013-11-22",      username: "Lena",
                  title: "Do something else", created: "9/22/2013"},
        {id: 107, complete: true,             priority: "high",
                  dueDate: "2013-11-22",      username: "Mike",
                  title: "Fix the foo",       created: "9/22/2013"},
        {id: 108, complete: false,            priority: "low",
                  dueDate: "2013-11-15",      username: "Punam",
                  title: "Adjust the bar",    created: "9/25/2013"},
        {id: 110, complete: false,            priority: "medium",
                  dueDate: "2013-11-15",      username: "Scott",
                  title: "Rename everything", created: "10/2/2013"},
        {id: 112, complete: true,             priority: "high",
                  dueDate: "2013-11-27",      username: "Lena",
                  title: "Alter all quuxes",  created: "10/5/2013"}
        // , ...
    ]
};

需求是找到Scott所有未完成的任务,并按照到期日期升序排列。

正确的结果是

[
    {id: 110, title: "Rename everything", 
        dueDate: "2013-11-15", priority: "medium"},
    {id: 104, title: "Do something", 
        dueDate: "2013-11-29", priority: "high"}
]

命令式的代码如下

getIncompleteTaskSummaries = function(membername) {
    return fetchData()
        .then(function(data) {
            return data.tasks;
        })
        .then(function(tasks) {
            var results = [];
            for (var i = 0, len = tasks.length; i < len; i++) {
                if (tasks[i].username == membername) {
                    results.push(tasks[i]);
                }
            }
            return results;
        })
        .then(function(tasks) {
            var results = [];
            for (var i = 0, len = tasks.length; i < len; i++) {
                if (!tasks[i].complete) {
                    results.push(tasks[i]);
                }
            }
            return results;
        })
        .then(function(tasks) {
            var results = [], task;
            for (var i = 0, len = tasks.length; i < len; i++) {
                task = tasks[i];
                results.push({
                    id: task.id,
                    dueDate: task.dueDate,
                    title: task.title,
                    priority: task.priority
                })
            }
            return results;
        })
        .then(function(tasks) {
            tasks.sort(function(first, second) {
                var a = first.dueDate, b = second.dueDate;
                return a < b ? -1 : a > b ? 1 : 0;
            });
            return tasks;
        });
};

pointfree的代码

var getIncompleteTaskSummaries = function(membername) {
  return fetchData()
    .then(R.prop("tasks"))
    .then(R.filter(R.propEq("username", membername)))
    .then(R.reject(R.propEq("complete", true)))
    .then(R.map(R.pick(["id", "dueDate", "title", "priority"])))
    .then(R.sortBy(R.prop("dueDate")));
};

pointfree的声明式的代码

// 提取 tasks 属性
var SelectTasks = R.prop("tasks");

// 过滤出指定的用户
var filterMember = member => R.filter(
  R.propEq("username", member)
);

// 排除已经完成的任务
var excludeCompletedTasks = R.reject(R.propEq("complete", true));

// 选取指定属性
var selectFields = R.map(
  R.pick(["id", "dueDate", "title", "priority"])
);

// 按照到期日期排序
var sortByDueDate = R.sortBy(R.prop("dueDate"));

// 合成函数
var getIncompleteTaskSummaries = function(membername) {
  return fetchData().then(
    R.pipe(
      SelectTasks,
      filterMember(membername),
      excludeCompletedTasks,
      selectFields,
      sortByDueDate,
    )
  );
};

参考文章

Pointfree编程风格指南

Favoring Curry

JS函数式编程指南

Tacit programming

Thinking in Ramda: Pointfree Style

Thinking in Ramda: Declarative Programming

我在github https://github.com/zhuanyongx...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/94592.html

相关文章

  • JavaScript函数编程,真香组合(一)

    摘要:组合的概念是非常直观的,并不是函数式编程独有的,在我们生活中或者前端开发中处处可见。其实我们函数式编程里面的组合也是类似,函数组合就是一种将已被分解的简单任务组织成复杂的整体过程。在函数式编程的世界中,有这样一种很流行的编程风格。 JavaScript函数式编程,真香之认识函数式编程(一) 该系列文章不是针对前端新手,需要有一定的编程经验,而且了解 JavaScript 里面作用域,闭...

    mengbo 评论0 收藏0
  • JavaScript 函数编程(二)

    摘要:注意是单一参数柯里化是由以逻辑学家命名的,当然编程语言也是源自他的名字,虽然柯里化是由和发明的。辨别类型和它们的含义是一项重要的技能,这项技能可以让你在函数式编程的路上走得更远。 slide 地址 三、可以,这很函数式~ showImg(https://segmentfault.com/img/remote/1460000015978685?w=187&h=160); 3.1.函数是一...

    thursday 评论0 收藏0
  • Js-函数编程

    摘要:组合组合的功能非常强大,也是函数式编程的一个核心概念,所谓的对过程进行封装很大程度上就是依赖于组合。在理解之前,先认识一个东西概念容器容器为函数式编程里普通的变量对象函数提供了一层极其强大的外衣,赋予了它们一些很惊艳的特性。 前言 JavaScript是一门多范式语言,即可使用OOP(面向对象),也可以使用FP(函数式),由于笔者最近在学习React相关的技术栈,想进一步深入了解其思想...

    whinc 评论0 收藏0
  • 前端_JavaScript

    摘要:为此决定自研一个富文本编辑器。例如当要转化的对象有环存在时子节点属性赋值了父节点的引用,为了关于函数式编程的思考作者李英杰,美团金融前端团队成员。只有正确使用作用域,才能使用优秀的设计模式,帮助你规避副作用。 JavaScript 专题之惰性函数 JavaScript 专题系列第十五篇,讲解惰性函数 需求 我们现在需要写一个 foo 函数,这个函数返回首次调用时的 Date 对象,注意...

    Benedict Evans 评论0 收藏0
  • JS每日一题:函数编程中代码组合(compose)如何理解?

    摘要:期函数式编程中代码组合如何理解定义顾名思义,在函数式编程中,就是将几个有特点的函数拼凑在一起,让它们结合,产生一个崭新的函数代码理解一个将小写转大写的函数一个在字符后加的函数将两个函数组合起来这里假设我们实现了每日一题每日一题显示结果里上面 20190315期 函数式编程中代码组合(compose)如何理解? 定义: 顾名思义,在函数式编程中,Compose就是将几个有特点的函数拼凑在...

    Kaede 评论0 收藏0

发表评论

0条评论

zorpan

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<