• 如何使用函数式编程?


    由于最近面试的原因,一直没有更新博文,最近有时间了,谈一下一直在研究的函数式编程的使用。

    函数式编程,从接触以来似乎没怎么使用,据说backbone的依赖是underscore,是不是也是函数式呢?redux不依赖underscore,是不是函数式呢?

    backbone这个不清楚,反正在redux中或多或少的使用了函数式编程的理念,用到函数式编程(以下简称为fp)的compose函数来处理它的reducer。

    那么fp到底该怎么用,它有什么好处呢?                  系统的学习fp可到这里来-->点我 当然,买本《functional javascript》也是不错的~

    您也可以看一下我的这篇博文--《javascript柯里化及组合函数》里面讲了本例子所用到的主要函数~

    -----------------------正文分割线-------------------------------------------

    引用一句话说:我们要开始转变观念了,从现在开始,我们将不再指示计算机如何工作,而是指出我们明确希望得到的结果。

    先举个栗子~

     这是命令式编程

    var makes = [];
    for (i = 0; i < cars.length; i++) {
      makes.push(cars[i].make);
    }

    这是声明式编程

    var makes = cars.map(function(car){ return car.make; });

    我觉得高低立分,声明式的map函数是一个表达式,直观而且自由度很大。我们可以随意更改makes的内容,而这些都是命令式达不到的。fp的优势就是这样。

    从书上找一个例子来说明fp是怎样编程的:

    这个例子是获取图片,然后展示在浏览器上。

    这是html结构:

    <!DOCTYPE html>
    <html>
      <head>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.11/require.min.js"></script>
        <script src="flickr.js"></script>
      </head>
      <body></body>
    </html>

    flickr.js的内容:

    requirejs.config({
      paths: {
        ramda: 'https://cdnjs.cloudflare.com/ajax/libs/ramda/0.13.0/ramda.min',
        jquery: 'https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min'
      }
    });
    
    require([
        'ramda',
        'jquery'
      ],
      function (_, $) {
        var trace = _.curry(function(tag, x) {
          console.log(tag, x);
          return x;
        });
        // app goes here
      });

    ramda是一个fp的库,就类似于underscore和lodash,本例子用了requirejs,虽然我不怎么喜欢这个框架,为保持一致性,就一直用require了。

    这个flickr.js就是用require加载了ramda和jquery,声明了trace函数用来在fp的流中打印当前流的值,其实排错用,暂时忽略它。

    本应用的任务呢?

    1. 根据特定搜索关键字构造 url
    2. 向 flickr 发送 api 请求
    3. 把返回的 json 转为 html 图片
    4. 把图片放到屏幕上

    上面提到了2个不纯的动作:从api获取数据和把图片放到屏幕上,我们先把不纯的动作写出来,以便分离开其他纯的动作。

    var Impure = {
      getJSON: _.curry(function(callback, url) {
        $.getJSON(url, callback);
      }),
    
      setHtml: _.curry(function(sel, html) {
        $(sel).html(html);
      })
    };

    在 Impure 命名空间下,这样我们就知道它们都是危险函数。

    简单了封装了jquery的getJSON和jquery的html。就可以用Impure.getJSON(url,callback)来调用啦。

    var url = function (term) {
      return 'https://api.flickr.com/services/feeds/photos_public.gne?tags=' + term + '&format=json&jsoncallback=?';
    };

    封装获得url的函数。

    好,我们把方法都写完了,现在开始fp之旅。

    利用compose组合了获取url和获取图片这个任务。app("cats");就可以得到cats图片的src,回掉函数是trace("response"),它会打印服务器的返回。

    var app = _.compose(Impure.getJSON(trace("response")), url);
    
    app("cats");

    如果命令式编程的话会写为

    var _url = url("cat");
    Impure.getJSON(_url,trace("response"));

    这样就不是那么简洁自由了。

    这会调用 url 函数,然后把字符串传给 getJSON 函数。getJSON 已经局部应用了 trace,加载这个应用将会把请求的响应显示在 console 里。

    得到这个json后,我们想要从这个 json 里构造图片,但是 src 都在 items 数组中的每个 media 对象的 m 属性上

    然后实现一个辅助函数:

    var prop = _.curry(function(property, object){
      return object[property];
    });

    这个其实就是_.prop了。很无聊是不是。。

    然后呢,获取图片url,

    var mediaUrl = _.compose(_.prop('m'), _.prop('media'));
    
    var srcs = _.compose(_.map(mediaUrl), _.prop('items'));

    从json里面取到items属性,然后取media属性,然后取m属性。。一系列的取值运算都用compose来执行。

    然后把它整合到html里:

    var renderImages = _.compose(Impure.setHtml("body"), srcs);
    var app = _.compose(Impure.getJSON(renderImages), url);

    app方法是把字符串通过url形成需要捕获的url字符串,然后放到getJSON里获取,然后返回renderImage方法。

    renderImage函数是把得到的src传到body里,body里会显示图片的src。

    这个方法是真正把图片添加到html里。

    var img = function (url) {
      return $('<img />', { src: url });
    };

    这是真正render图片的renderImages方法。与上面的renderImages方法不一样的地方就是在setHtml的时候不是把srcs放到body了,它加了img标签了。

    images就是把srcs组装成html里的img标签。

    var images = _.compose(_.map(img), srcs);
    var renderImages = _.compose(Impure.setHtml("body"), images);
    var app = _.compose(Impure.getJSON(renderImages), url);

    这样就完成了~

    最后执行

    app("cats");

    的时候,这一系列工具链启动了,cats传入url里面封装成需要捕获的url,然后传到getJSON里面获取。getJSON的callback又是renderImages,renderImages把得到的src组装到img标签,然后扔到body里。然后这些工作就完成了。

    只说做什么,不说怎么做。这就是fp。我们只传入了cats,这是我们需要的cat图片,然后怎么做呢,这些函数通过compose完成自己的功能。然后通过compose组合起来,完成这个项目的应用。

    是不是觉得清晰了很多,而且每一步都是可变的,就像gulp执行pipe,增加一个流的处理是很简单的,对管道的流的处理可以改变整个结果。

    完整代码:

    requirejs.config({
      paths: {
        ramda: 'https://cdnjs.cloudflare.com/ajax/libs/ramda/0.13.0/ramda.min',
        jquery: 'https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min'
      }
    });
    
    require([
        'ramda',
        'jquery'
      ],
      function (_, $) {
        ////////////////////////////////////////////
        // Utils
    
        var Impure = {
          getJSON: _.curry(function(callback, url) {
            $.getJSON(url, callback);
          }),
    
          setHtml: _.curry(function(sel, html) {
            $(sel).html(html);
          })
        };
    
        var img = function (url) {
          return $('<img />', { src: url });
        };
    
        var trace = _.curry(function(tag, x) {
          console.log(tag, x);
          return x;
        });
    
        ////////////////////////////////////////////
    
        var url = function (t) {
          return 'https://api.flickr.com/services/feeds/photos_public.gne?tags=' + t + '&format=json&jsoncallback=?';
        };
    
        var mediaUrl = _.compose(_.prop('m'), _.prop('media'));
    
        var srcs = _.compose(_.map(mediaUrl), _.prop('items'));
    
        var images = _.compose(_.map(img), srcs);
    
        var renderImages = _.compose(Impure.setHtml("body"), images);
    
        var app = _.compose(Impure.getJSON(renderImages), url);
    
        app("cats");
      });
  • 相关阅读:
    rm 、git rm 、git rm --cached的区别
    实行敏捷开发阶段性总结
    [转]Pycharm 断点调试方法
    scp -v 查看具体的过程
    根据字典的值大小进行排序
    文件服务器满故障排查总结
    看谷歌白板面试有感
    [译]从列表或字典创建Pandas的DataFrame对象
    查找docker log久远数据方法
    docker log 批量删除报错: find: `/var/lib/docker/containers/': 没有那个文件或目录
  • 原文地址:https://www.cnblogs.com/dh-dh/p/5405571.html
Copyright © 2020-2023  润新知