DEV Community

Cover image for บันทึก FunctionalPrograming
Nantipat
Nantipat

Posted on

1

บันทึก FunctionalPrograming

JavaScript ES6 (ES2015)

Arrow function

// ES6
[1, 2, 3].map(num => num * 2);

// ES5
[1, 2, 3].map(function(num) {
  return num * 2;
});

// ES6
app.get('/', (req, res) => {  
  res.json({
    message: 'Hello World'
  });
});

// ES5
app.get('/', function(req, res) {  
  res.json({
    message: 'Hello World'
  });
});


() => Hello
() => {} (แบบ code block)
(num) => num * 2
(num1, num2) => {return num1 + num2;} (แบบ code block)
() => ({ foo: bar }) (return เป็น Object ให้ใช้ปีกกา)
Enter fullscreen mode Exit fullscreen mode

ลองเล่น

const f = x => x * 2
print(f(2));

//===========================
var materials = [
  'Hydrogen',
  'Helium',
  'Lithium',
  'Beryllium'
];

//จัดให้ดูง่ายสำหรับผม
const a = materials.map(
    function(material) {
        return material.length;
    }
);

//เปลื่อน function เป็น =>  ไว้ด้านหลังแทน
const b = materials.map(
    (material) => {
      return material.length;
    }
);

// ลบ  () ของ parameter ออก
รวมถึง {},return ของ Function   
materials.map(material => material.length);
//output [8, 6, 7, 9]


//====================================
var simple = a => a > 15 ? 15 : a;

print(simple(16));
print(simple(10));
// 15 10


const a = (a,b=10) => a*b
print(a(2));
// 20


//  ต้องใส่ () ด้วย ถ้าจะ Default
const a = (a=5) => a
print(a());
Enter fullscreen mode Exit fullscreen mode

nested arrow functions

let add = (x,y) => x + y;
add(2,3); //=> 5


let add = x => y => x + y;
add(2)(3);


let add = function (x) {
  return function (y) {
    return x + y;
  };
};
Enter fullscreen mode Exit fullscreen mode

What is Functional Programming

แบบธรรมดา

let numbers = [1, 2, 3, 4, 5, 6 ,7, 8, 9, 10];
let squaredEvens = [];
for(let i = 0; i < numbers.length; i++) {
  let currentNumber = numbers[i];
  if(currentNumber % 2 === 0) {
    squaredEvens.push(currentNumber * currentNumber)
  }
}
Enter fullscreen mode Exit fullscreen mode

แบบ FP

const numbers = [1, 2, 3, 4, 5, 6 ,7, 8, 9, 10];
const isEven = n => n % 2 === 0;
const square = n => n * n;
numbers.filter(isEven).map(square);
Enter fullscreen mode Exit fullscreen mode

Note

filter รับ parameter เป็น Function ที่โยนไปค่าจาก array ไปเชคทีละตัว

Same

function isEven(n) { return n % 2 === 0; }
function square(n) { return n * n; }
Enter fullscreen mode Exit fullscreen mode

หัวใจสำคัญของ Functional Programming

หลีกเลี่ยง side-effect หรือผลข้างเคียงที่จะเกิดต่อ function อื่นและต่อตัวเอง

  • เมื่อมี input ค่าหนึ่ง จะได้ต้องได้ output เท่าเดิมเสมอ

  • ไม่ไปเปลี่ยนแปลงค่าของตัวแปรจำพวก global variable หรือ static variable

## First-class function & Higher-order function

  • First-class function คือ feature ของ programming language ที่อนุญาตให้ function นั้นเป็น first-class citizen ประชากรอันดับหนึ่งของภาษา เทียบเท่ากับ value อื่น ๆ อย่างตัวเลข หรือ string เป็นต้น ซึ่งเราสามารถ assign function ให้กับตัวแปรได้ เป็น argument หรือ return value ของ function อื่นๆก็ได้

  • function ในลักษณะที่รับ argument เป็น function หรือ return function ออกมามีชื่อเรียกว่า Higher-order function

## Lambda expression & Closure

  • ตัวแปร (ในเชิงคณิตศาสตร์ ซึ่งเป็นตัวแทนของค่า หรือ argument ของ function ไม่ใช่ mutable variable หรือตัวแปรแบบที่เรา update ค่าได้เรื่อยๆเวลาเขียนโปรแกรมแบบ imperative)

    • function ซึ่งใน Lambda calculus นั้น function ไม่มีชื่อ
    • application ([function][argument]) ถ้าเปรียบเทียบกับพีชคณิตก็คล้ายๆกับแทนค่า parameter ใน function เช่น ƒ(3), g(21)
 const foo = [1,2,3];
 const baz = foo.map(function bar (n) { return n + 1; });
Enter fullscreen mode Exit fullscreen mode

ส่วน Closure เป็นสิ่งที่ผมสงสัยอยู่นานมาก เพราะมักจะเจอตัวอย่างแบบนี้

 function ticker() {
   let count = 0;
   return () => count++;
 }
 let t = ticker();
 t(); // 0
 t(); // 1
 t(); // 2
 t(); // 3
Enter fullscreen mode Exit fullscreen mode

เขาบอกว่า Closures คือ functions ที่อ้างอิง free variables เจ้า free variables เนี่ย มันคือตัวแปรที่ไม่ได้ถูกประกาศไว้ใน parameter จากตัวอย่างด้านบน จะเห็นได้ว่า () => count++ ไม่มี parameter เลย แต่มีตัวแปร count ซึ่งโดยปกติแล้วเมื่อ execute ticker เสร็จ เจ้า count ควรจะถูก garbage collector เก็บไป แต่ในกรณีนี้ เมื่อเราบอกว่า let t = ticker(); แล้ว t จะกลายเป็น closure ซึ่งเป็น function ที่จำสภาพแวดล้อมที่สร้างมันขึ้นมา ทำให้ผลลัพธ์เป็นอย่างที่เห็น แต่ไม่เป็น FP

 // Original
 function makeAdder(a) {
   return b => a + b;
 }
 const addFive = makeAdder(5);
 const addTen = makeAdder(10);
 addFive(20); // 5 + 20 => 25
 addTen(9); // 10 + 9 => 19


 // Refactoring
 const makeAdder2  = a => b => a + b;
 const addfuck = makeAdder2(5)
 addfuck(20)
Enter fullscreen mode Exit fullscreen mode

คราวนี้จะเห็นได้ว่าทั้ง makeAdder, addFive และ addTen ล้วนเป็น pure function หรือตัวอย่างที่ดูฉลาดขึ้นหน่อย

 const makeSum = transFunc  => (a, b) =>
 transFunc(a) + transFunc(b);

const sumSquared = makeSum(n => n * n);
const sumCubed = makeSum(n => n * n * n);

sumSquared(1, 2); // 1^2 + 2^2 => 5
sumCubed(1, 2); // 1^3 + 2^3 => 9
Enter fullscreen mode Exit fullscreen mode

## รู้จักกับ Javascript Callback Function
### แบบธรรมดา (sync )

console.log('Step 1');
funcSync();
console.log('Step 3');

function funcSync () {
  console.log('Step 2');
}
Enter fullscreen mode Exit fullscreen mode

### แบบไม่ธรรมดา (async )

 setTimeout(function(){
   console.log('Step 1')
 }, 3000);

 console.log('Step 2')

 // ผลลัพท์
 // Step 2
 // Step 1

setTimeout(doSomeThing, 3000);

function doSomeThing () {
  console.log('Hello')
}
Enter fullscreen mode Exit fullscreen mode

ฟังชั่น setTimeout จะมีการรับ parameter อยู่ 2 ตัวก็คือ
Function ที่จะให้ทำงานเมื่อถึงเวลาที่กำหนด(หรือก็คือ Callback Function นั่นเอง)
Integer ที่ใช้กำหนดเวลา มีหน่วยเป็น ms

 const myAsyncFunc = callback => callback();

myAsyncFunc(callbackFunc);

function callbackFunc () {
  print('this is callback function')
}
Enter fullscreen mode Exit fullscreen mode

มันจะทำ function ที่ใส่เข้าไปให้เสร็จก่อนแล้วค่อยออกมา

ลองเล่น

Example 1

// Original
asyncFunc(
          function (str)
          {
              print('cb1 : ' + str);
          },
          function (str)
          {
              print('cb2 : ' + str);
          }
    );

function asyncFunc (cb1, cb2) {
  cb1('A');
  cb2('B');
}

// Refactoring

let  asyncFunc = (cb1,cb2) =>  {
    cb1('A');
    cb2('B');
}

const  f1 = str => print('cb1 : ' + str);
const  f2 = str => print('cb2 : ' + str);

asyncFunc(f1,f2);
Enter fullscreen mode Exit fullscreen mode

Example 2

// Original
asyncFunc(cb, cb);

function asyncFunc (cb1, cb2) {
  cb1('A');
  cb2('B');
}

function cb (str) {
  print('cb : ' + str);
}

// Refactoring

const cb = str => print('cb : ' + str)

let  asyncFunc = (cb1,cb2) => {
    cb("A");
    cb("B");
}
asyncFunc(cb,cb);

Enter fullscreen mode Exit fullscreen mode

Example 3

// Original

asyncFunc(cb, cb);

function asyncFunc (running, done) {
  for (var i = 0;i<10;i++) {
    running('i = ' + i);
  }
  done('done');
}

function cb (str) {
  print(str);
}
// Refactoring
let  cb = str => print(str);
let asyncFunc = (running, done) =>{
    for (var i = 0;i<10;i++) {
    running('i = ' + i);
  }
  done('done');
}
asyncFunc(cb, cb);
Enter fullscreen mode Exit fullscreen mode

จัดการ Callback

Promise

เป็นเหมือนกับคำสัญญาว่าจะทำคำสั่งนี้ให้เรานะ โดยมันจะมีอยู่ 3 สถานะก็คือ pending, resolved, rejected เมื่อเริ่มต้นทำคำสั่ง promise จะมีสถานะเป็น pending ถ้าทำเสร็จแล้วจะมีสถานะเป็น resolved โดยจะทำคำสั่งถัดไปที่อยู่ใน .then() และถ้าทำคำสั่งไม่สำเร็จ จะมีสถานะเป็น rejected และจะไม่ทำคำสั่งถัดไป แต่จะทำคำสั่งที่อยู่ใน .catch() แทน

var p = new Promise(function(resolve, reject) {
    let a = 5;
    if(a >= 5) {
        resolve(a);
    }else {
        reject(a);
    }
}).then(function(a) {
    console.log(`result then : ${a}`);
    return a + 2;
}).then(function(b) {
    console.log(`result then : ${b}`);
}).catch(function(a) {
    console.log(`result catch : ${a}`);
});
Enter fullscreen mode Exit fullscreen mode

Note

การส่ง return จะไม่ออกมาข้างนอกนะ จะเข้า parameter then ถัดไป

async/await

const resolveAfter2Seconds = x => {
     return new Promise(
        resolve => {
            setTimeout(() => {
                resolve(x);
            }, 2000);
        }
    );
}
console.log(resolveAfter2Seconds(50));

async function add1(x) {
  const a = await resolveAfter2Seconds(20);
  const b = await resolveAfter2Seconds(30);
  return x + a + b;
}
console.log(add1(100));

add1(10).then(v => {
    console.log(v);  // prints 60 after 4 seconds.
});

async function add2(x) {
  const p_a = resolveAfter2Seconds(20);
  const p_b = resolveAfter2Seconds(30);
  return x + await p_a + await p_b;
}

add2(10).then(v => {
  console.log(v);  // prints 60 after 2 seconds.
});
Enter fullscreen mode Exit fullscreen mode

Function Composition

คือกระบวนการรวมกันของ functions มากกว่า 1 ขึ้นไป และก่อให้เกิด function ใหม่ขึ้นมา
หรือรูปแบบหนึ่งที่ใช้ในการอธิบายที่ง่ายที่สุดก็คือ f(g(x)) หรือการรวมกันของ function f และ g มากระทำกับ x

const compose = function(f, g) {
  return function(x) {
    return f(g(x))
  }
}

const compose = (f, g) => x => f(g(x))
Enter fullscreen mode Exit fullscreen mode

apply functions ไล่ไปจาก ขวา ไป ซ้าย

ตัวอย่าง

จินตนาการว่า เราจะสร้าง function ทำความสะอาด String ก่อนนำไปใช้งาน (sanitize function)

ขั้นตอนการทำงาน

ตัดช่องว่างที่ไม่จำเป็นหน้าหลังของคำ (trim)
แปลงคำทั้งหมดเป็นตัวพิมพ์เล็ก (trim)

  • ตัดช่องว่างที่ไม่จำเป็นหน้าหลังของคำ (trim)
  • แปลงคำทั้งหมดเป็นตัวพิมพ์เล็ก (trim)
function sanitize(str) {
  return str.trim().toLowerCase()
}
sanitize('  Hello My Name is Ham     ') // 'hello my name is ham'
Enter fullscreen mode Exit fullscreen mode

ทีนี้ เราลองเขียนแบบ FP กันดู

const trim = s => s.trim()
const toLowerCase = s => s.toLowerCase()
function sanitize(str) {
  return toLowerCase(trim(str))
}
sanitize('  Hello My Name is Ham     ') // 'hello my name is ham'
Enter fullscreen mode Exit fullscreen mode
const compose = (f, g) => x => f(g(x))
const sanitize = compose(toLowerCase, trim)
sanitize('  Hello My Name is Ham     ') // 'hello my name is ham'
Enter fullscreen mode Exit fullscreen mode
const trim = s => s.trim()
const toLowerCase = s => s.toLowerCase()
const join = separator => arr => arr.join(separator)
const split = separator => arr => arr.split(separator)
const toSlug = compose(
  toLowerCase,
  join('-'),
  split(' '),
  trim,
)
toSlug('   THIS is SluG    ') // 'this-is-slug'
Enter fullscreen mode Exit fullscreen mode

Pipe

ลักษณะการทำงานของ pipe คือ การส่งต่อ ผลลัพธ์ ที่ได้จากการ คำสั่งก่อนหน้า ไปให้แก่ คำสั่งด้านหลัง

  • compose: apply function จาก ขวา ไป ซ้าย
  • pipe: apply function จาก ซ้าย ไป ขวา
const toSlug = pipe(
  trim,
  split(' '),
  join('-'),
  toLowerCase,
)
toSlug('   THIS is SluG    ') // 'this-is-slug'
Enter fullscreen mode Exit fullscreen mode

https://github.com/NantipatSoftEn/FunctionalPrograming/blob/master/Higher-Order/README.md

SurveyJS custom survey software

JavaScript UI Libraries for Surveys and Forms

SurveyJS lets you build a JSON-based form management system that integrates with any backend, giving you full control over your data and no user limits. Includes support for custom question types, skip logic, integrated CCS editor, PDF export, real-time analytics & more.

Learn more

Top comments (2)

Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
nantipatsoften profile image
Nantipat

^_^ เขียนบันทึกไว้ครับ
ไม่ได้นึกว่าจะมีใครมาอ่าน

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay