国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

Home php教程 PHP開(kāi)發(fā) Use pure functional programming wisely

Use pure functional programming wisely

Nov 22, 2016 pm 12:25 PM

Functional programming can reduce the complexity of the program: a function looks like a mathematical formula. Learning functional programming can help you write simpler code with fewer bugs.

Pure function

A pure function can be understood as a function that must have the same output with the same input, without any observable side effects

//pure
function add(a + b) {
  return a + b;
}

The above is a pure function, it does not depend on or change any variables other than the function state, always returns the same output for the same input.

//impure
var minimum = 21;
var checkAge = function(age) {
  return age >= minimum; // 如果minimum改變,函數(shù)結(jié)果也會(huì)改變
}

This function is not a pure function because it relies on external mutable state

If we move the variable inside the function, then it becomes a pure function, so that we can ensure that the function can be compared correctly every time age.

var checkAge = function(age) {
  var minimum = 21;
  return age >= minimum;
};

Pure functions have no side effects, some things you need to remember is that it does not:

Access system state outside the function

Modify objects passed as parameters

Initiate http requests

Preserve user input

Querying the DOM

Controlled mutation

You need to pay attention to some mutation methods that will change arrays and objects. For example, you need to know the difference between splice and slice.

//impure, splice 改變了原數(shù)組
var firstThree = function(arr) {
  return arr.splice(0,3);
}

//pure, slice 返回了一個(gè)新數(shù)組
var firstThree = function(arr) {
  return arr.slice(0,3);
}

If we avoid using mutator methods on the objects passed into the function, our programs will be easier to understand, and we can reasonably expect that our functions will not change anything outside the function.

let items = ['a', 'b', 'c'];
let newItems = pure(items);
//對(duì)于純函數(shù)items始終應(yīng)該是['a', 'b', 'c']

Advantages of pure functions

Compared to impure functions, pure functions have the following advantages:

Easier to test, because their only responsibility is to calculate output based on input

The results can be cached, because the same Input will always get the same output

Self-documenting because the dependencies of the function are clear

Easier to call because you don’t have to worry about the side effects of the function

Because the results of pure functions can be cached, we can remember Keep them in place so that complex and expensive operations only need to be performed once when called. For example, caching the results of a large query index can greatly improve program performance.

Unreasonable pure function programming

Using pure functions can greatly reduce the complexity of the program. However, if we use too many abstract concepts of functional programming, our functional programming will also be very difficult to understand.

import _ from 'ramda';
import $ from 'jquery';

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

  setHtml: _.curry(function(sel, html) {
    $(sel).html(html);
  })
};

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

var url = function (t) {
  return &#39;http://api.flickr.com/services/feeds/photos_public.gne?tags=&#39; +
    t + &#39;&format=json&jsoncallback=?&#39;;
};

var mediaUrl = _.compose(_.prop(&#39;m&#39;), _.prop(&#39;media&#39;));
var mediaToImg = _.compose(img, mediaUrl);
var images = _.compose(_.map(mediaToImg), _.prop(&#39;items&#39;));
var renderImages = _.compose(Impure.setHtml("body"), images);
var app = _.compose(Impure.getJSON(renderImages), url);
app("cats");

Take a minute to understand the above code.

Unless you are exposed to these concepts of functional programming (currying, composition and props), it will be difficult to understand the above code. Compared with the purely functional approach, the following code is easier to understand and modify, it describes the program more clearly and requires less code.

The parameter of the app function is a tag string

Get JSON data from Flickr

Extract urls from the returned data

Create node array

Insert them into the document

var app = (tags) => {
  let url = `http://api.flickr.com/services/feeds/photos_public.gne?tags=${tags}&format=json&jsoncallback=?`;
  $.getJSON(url, (data) => {
    let urls = data.items.map((item) => item.media.m)
    let images = urls.map(url) => $(&#39;<img />&#39;, {src:url}) );
    
    $(document.body).html(images);
  })
}
app("cats");

Or you can use fetch and Promise to better perform asynchronous operations.

let flickr = (tags)=> {
  let url = `http://api.flickr.com/services/feeds/photos_public.gne?tags=${tags}&format=json&jsoncallback=?`
  
  return fetch(url)
    .then((resp)=> resp.json())
    .then((data)=> {
      let urls = data.items.map((item)=> item.media.m )
      let images = urls.map((url)=> $(&#39;<img />&#39;, { src: url }) )

      return images
  })
}
flickr("cats").then((images)=> {
  $(document.body).html(images)
})

Ajax requests and DOM operations are not pure, but we can form the remaining operations into pure functions and convert the returned JSON data into an array of image nodes.

let responseToImages = (resp) => {
  let urls = resp.items.map((item) => item.media.m)
  let images = urls.map((url) => $(&#39;<img />&#39;, {src:url}))
  
  return images
}

Our function does 2 things:

Convert the returned data into urls

Convert urls into image nodes

The functional approach is to split the above 2 tasks, and then use compose to combine a function The result is passed as a parameter to another parameter.

let urls = (data) => {
  return data.items.map((item) => item.media.m)
}
let images = (urls) => {
  return urls.map((url) => $(&#39;<img />&#39;, {src: url}))
}
let responseToImages = _.compose(images, urls)

compose returns a combination of functions, each function will use the result of the latter function as its own input parameter

What compose does here is to pass the result of urls into the images function

let responseToImages = (data) => {
  return images(urls(data))
}

By changing the code into Making them pure functions gives us the opportunity to reuse them in the future, and they are easier to test and self-documenting. The bad thing is that when we overuse these functional abstractions (like in the first example), it complicates things, which is not what we want. The most important thing to ask yourself when we refactor code is:

Does this make the code easier to read and understand?

基本功能函數(shù)

我并不是要詆毀函數(shù)式編程。每個(gè)程序員都應(yīng)該齊心協(xié)力去學(xué)習(xí)基礎(chǔ)函數(shù),這些函數(shù)讓你在編程過(guò)程中使用一些抽象出的一般模式,寫(xiě)出更加簡(jiǎn)潔明了的代碼,或者像Marijn Haverbeke說(shuō)的

一個(gè)程序員能夠用常規(guī)的基礎(chǔ)函數(shù)武裝自己,更重要的是知道如何使用它們,要比那些苦思冥想的人高效的多。--Eloquent JavaScript, Marijn Haverbeke

這里列出了一些JavaScript開(kāi)發(fā)者應(yīng)該掌握的基礎(chǔ)函數(shù)
Arrays
-forEach
-map
-filter
-reduce

Functions
-debounce
-compose
-partial
-curry

Less is More

讓我們來(lái)通過(guò)實(shí)踐看一下函數(shù)式編程能如何改善下面的代碼

let items = [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;];
let upperCaseItems = () => {
  let arr = [];
  for (let i=0, ii= items.length; i<ii; i++) {
    let item = items[i];
    arr.push(item.toUpperCase());
  }
  items = arr;
}

共享狀態(tài)來(lái)簡(jiǎn)化函數(shù)

這看起來(lái)很明顯且微不足道,但是我還是讓函數(shù)訪問(wèn)和修改了外部的狀態(tài),這讓函數(shù)難以測(cè)試且容易出錯(cuò)。

//pure
let upperCaseItems = (items) => {
  let arr = [];
  for (let i =0, ii= items.length; i< ii; i++) {
    let item = items[i];
    arr.push(item.toUpperCase());
  }
  return arr;
}

使用更加可讀的語(yǔ)言抽象forEach來(lái)迭代

let upperCaseItems = (items) => {
  let arr = [];
  items.forEach((item) => {
    arr.push(item.toUpperCase());
  })
  return arr;
}

使用map進(jìn)一步簡(jiǎn)化代碼

let upperCaseItems = (items) => {
  return items.map((item) => item.toUpperCase())
}

進(jìn)一步簡(jiǎn)化代碼

let upperCase = (item) => item.toUpperCase()
let upperCaseItems = (item) => items.map(upperCase)

刪除代碼直到它不能工作

我們不需要為這種簡(jiǎn)單的任務(wù)編寫(xiě)函數(shù),語(yǔ)言本身就提供了足夠的抽象來(lái)完成功能

let items = [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;]
let upperCaseItems = item.map((item) => item.toUpperCase())

測(cè)試

純函數(shù)的一個(gè)關(guān)鍵優(yōu)點(diǎn)是易于測(cè)試,所以在這一節(jié)我會(huì)為我們之前的Flicker模塊編寫(xiě)測(cè)試。

我們會(huì)使用Mocha來(lái)運(yùn)行測(cè)試,使用Babel來(lái)編譯ES6代碼。

mkdir test-harness
cd test-harness
npm init -y
npm install mocha babel-register babel-preset-es2015 --save-dev
echo &#39;{ "presets": ["es2015"] }&#39; > .babelrc
mkdir test
touch test/example.js

Mocha提供了一些好用的函數(shù)如describe和it來(lái)拆分測(cè)試和鉤子(例如before和after這種用來(lái)組裝和拆分任務(wù)的鉤子)。assert是用來(lái)進(jìn)行相等測(cè)試的斷言庫(kù),assert和assert.deepEqual是很有用且值得注意的函數(shù)。

讓我們來(lái)編寫(xiě)第一個(gè)測(cè)試test/example.js

import assert from &#39;assert&#39;;

describe(&#39;Math&#39;, () => {
  describe(&#39;.floor&#39;, () => {
    it(&#39;rounds down to the nearest whole number&#39;, () => {
      let value = Math.floor(4.24)
      assert(value === 4)
    })
  })
})

打開(kāi)package.json文件,將"test"腳本修改如下

mocha --compilers js:babel-register --recursive

然后你就可以在命令行運(yùn)行npm test

Math
  .floor
    ? rounds down to the nearest whole number
1 passing (32ms)

Note:如果你想讓mocha監(jiān)視改變,并且自動(dòng)運(yùn)行測(cè)試,可以在上述命令后面加上-w選項(xiàng)。

mocha --compilers js:babel-register --recursive -w

測(cè)試我們的Flicker模塊

我們的模塊文件是lib/flickr.js

import $ from &#39;jquery&#39;;
import { compose } from &#39;underscore&#39;;

let urls = (data) => {
  return data.items.map((item) => item.media.m)
}

let images = (urls) => {
  return urls.map((url) => $(&#39;<img />&#39;, {src: url})[0] )
}

let responseToImages = compose(images, urls)

let flickr = (tags) => {
  let url = `http://api.flickr.com/services/feeds/photos_public.gne?tags=${tags}&format=json&jsoncallback=?`
  
  return fetch(url)
    .then((response) => reponse.json())
    .then(responseToImages)
}

export default {
  _responseToImages: responseToImages,
  flickr: flickr
}

我們的模塊暴露了2個(gè)方法:一個(gè)公有flickr和一個(gè)私有函數(shù)_responseToImages,這樣就可以獨(dú)立的測(cè)試他們。

我們使用了一組依賴:jquery,underscore和polyfill函數(shù)fetch和Promise。為了測(cè)試他們,我們使用jsdom來(lái)模擬DOM對(duì)象window和document,使用sinon包來(lái)測(cè)試fetch api。

npm install jquery underscore whatwg-fetch es6-promise jsdom sinon --save-dev
touch test/_setup.js

打開(kāi)test/_setup.js,使用全局對(duì)象來(lái)配置jsdom

global.document = require(&#39;jsdom&#39;).jsdom(&#39;<html></html>&#39;);
global.window = document.defaultView;
global.$ = require(&#39;jquery&#39;)(window);
global.fetch = require(&#39;whatwg-fetch&#39;).fetch;

我們的測(cè)試代碼在test/flickr.js,我們將為函數(shù)的輸出設(shè)置斷言。我們"stub"或者覆蓋全局的fetch方法,來(lái)阻斷和模擬HTTP請(qǐng)求,這樣我們就可以在不直接訪問(wèn)Flickr api的情況下運(yùn)行我們的測(cè)試。

import assert from &#39;assert&#39;;
import Flickr from &#39;../lib/flickr&#39;;
import sinon from &#39;sinon&#39;;
import { Promise } from &#39;es6-promise&#39;;
import { Response } from &#39;whatwg-fetch&#39;;

let sampleResponse = {
  items: [{
    media: { m: &#39;lolcat.jpg&#39; }
  }, {
    media: {m: &#39;dancing_pug.gif&#39;}
  }]
}

//實(shí)際項(xiàng)目中我們會(huì)將這個(gè)test helper移到一個(gè)模塊里
let jsonResponse = (obj) => {
  let json = JSON.stringify(obj);
  var response = new Response(json, {
    status: 200,
    headers: {&#39;Content-type&#39;: &#39;application/json&#39;}
  });
  return Promise.resolve(response);
}


describe(&#39;Flickr&#39;, () => {
  describe(&#39;._responseToImages&#39;, () => {
    it("maps response JSON to a NodeList of <img>", () => {
      let images = Flickr._responseToImages(sampleResponse);
      
      assert(images.length === 2);
      assert(images[0].nodeName === &#39;IMG&#39;);
      assert(images[0].src === &#39;lolcat.jpg&#39;);
    })
  })
  
  describe(&#39;.flickr&#39;, () => {
    //截?cái)鄁etch 請(qǐng)求,返回一個(gè)Promise對(duì)象
    before(() => {
      sinon.stub(global, &#39;fetch&#39;, (url) => {
        return jsonResponse(sampleResponse)
      })
    })
    
    after(() => {
      global.fetch.restore();
    })
    
    it("returns a Promise that resolve with a NodeList of <img>", (done) => {
      Flickr.flickr(&#39;cats&#39;).then((images) => {
        assert(images.length === 2);
        assert(images[1].nodeName === &#39;IMG&#39;);
        assert(images[1].src === &#39;dancing_pug.gif&#39;);
        done();
      })
    })
  })  
  
})

運(yùn)行npm test,會(huì)得到如下結(jié)果:

Math
  .floor
    ? rounds down to the nearest whole number

Flickr
  ._responseToImages
    ? maps response JSON to a NodeList of <img>
  .flickr
    ? returns a Promise that resolves with a NodeList of <img>

3 passing (67ms)

到這里,我們已經(jīng)成功的測(cè)試了我們的模塊以及組成它的函數(shù),學(xué)習(xí)到了純函數(shù)以及如何使用函數(shù)組合。我們知道了純函數(shù)與不純函數(shù)的區(qū)別,知道純函數(shù)更可讀,由小函數(shù)組成,更容易測(cè)試。相比于不太合理的純函數(shù)式編程,我們的代碼更加可讀、理解和修改,這也是我們重構(gòu)代碼的目的。

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undress AI Tool

Undress AI Tool

Undress images for free

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)