Functional Programming là gì?

Em không biết đặt title như thế này đúng không. Nếu có gì sai sót mong staff/moderator sửa dùm em ://

Em đang học OOP thì nghe tới FP(Functional Programming), thấy tò mò nên thử search Google. Sau một thời gian mò mẫm, thì em biết được vài cái về FP:

  • Pure function:

Từ các inputs giống nhau => outputs giống nhau.
Không có side-effect

  • Tổng hợp các hàm trong một hàm

Và nhiều thức khác mà em không hiểu. Mong các bác giải thích nhiều hơn về FP :)))

NHƯNG, em sẽ không trọng tâm vào FP quá nhiều, chủ yếu là OOP.

Mình cũng ko rõ nhưng function programing nó theo kiểu declarative, nghĩa là lập trình viên sẽ nói họ muốn làm gì chứ không chỉ rõ là làm như thế nào như imperative.
Ví dụ như, tính tổng mảng số nguyên đi.

  • imperative: khai báo 1 biến sum, sau đó dùng vòng for duyệt qua từng phần tử của mảng, với mỗi phần tử ta sẽ cộng giá trị của phần tử đó vào biến sum.
  • declarative: tổng sẽ là tổng của phần tử đầu tiên với tổng của mảng ngoại trừ phần tử đầu tiên. (giống với đệ quy)

Hoăc 1 ví dụ khác về declarative là các câu truy vấn trong sql, ví dụ muốn lấy danh sách những người sinh năm 2020 thì mình chỉ cần nói là select * from student where dob=2020 tương đương với lấy những học sinh có năm sinh bằng 2020 trong bảng student, chứ ko cần chỉ ra cách làm là: khai báo một mảng result với giá trị khởi tạo là rỗng, sau đó vào bảng student duyệt qua danh sách học sinh, nếu học sinh nào có năm sinh bằng 2020 thì thêm vào mảng result.

2 Likes

Hi cậu :smiley:

Cậu có thể đưa ra cậu không hiểu gì được không?
Tớ nghĩ cậu đã tìm hiểu qua và hiểu được FP là gì rồi, cũng đã hiểu pure function là gì, side effect là gì, nên tớ sẽ không cố gắng “reinvent the wheel”, mà đi thẳng vào vấn đề luôn để tiết kiệm thời gian cho tất cả mọi người :slight_smile:

7 Likes

Thực ra, minh chỉ hiểu FP chỉ là tận dụng function hết mức có thể, và mình chỉ muốn biết sử dụng FP như thế nào.

(Do mình đang học cấp 2 với lại mới học code nên hay hỏi mấy câu ngu ngu, mong mọi người thông cảm)

FP có mấy điểm chính: stateless và composition. Stateless thì gần như ngược lại vs OOP là stateful. Khi bạn đạt đc stateless thì k phải theo dõi xem biến rhay đổi ở đâu, k phải lo race condition… Composition là cách để kết hợp các phần lại vs nhau để tạo ra 1 phần lớn hơn phức tạp hơn, như chơi lego ấy :v. Như tên gọi thì FP bắt đầu bằng cách suy nghĩ về function trước, OOP thì bắt đầu nghĩ về object trước. Cá nhân mình thì tập trung vào FP và mình k thích OOP =))

7 Likes

Cá nhân mình thì lại thích OOP. Do là vì mindset(và do là thích được căng não :v )

1 Like

Căng não nghĩa là gì nhỉ :v. Bạn cứ thử code mà k dùng biến xem có căng não ko :)))

Bạn thử code asm với arm xem :smiley:

3 Likes

Cậu khiêm tốn quá :smiley:
Để tớ thử giải thích cách sử dụng FP thế nào xem có giúp cậu hiểu được không nhé? :slight_smile:

Problem

Không có gì tốt hơn để giải thích bằng việc đưa ra 1 ví dụ. Giờ cậu thử tưởng tượng cậu phải giải quyết 1 bài toán nho nhỏ thế này:

  • Input: List các số {1, 2, 3, 4, 5}
  • Output: danh sách mới, chứa các số gấp đôi của từng phần tử trong danh sách cũ: {2, 4, 6, 8, 10}

Rồi, giờ cậu biết FP - Functional programming rồi. Nó là 1 mô thức lập trình (paradigm - dịch ra tiếng Việt hơi củ chuối, cơ mà cứ dùng tạm thế đi :smiley: ) mà trong đó, function là công dân hạng nhất (first-class citizen).

Okay, giờ để sử dụng FP, cậu sẽ cần 1 thứ trước.
Cậu cần phải định nghĩa ra 1 hệ thống phức tạp hơn, nơi mà cậu sẽ có thể dựa vào đó để gắn các hàm của cậu vào để xử lý dữ liệu. Hợp lý thôi, mô thức chúng ta đang sử dụng gọi là lập trình hàm:slight_smile:

High order function

Hệ thống đó được gọi là high order function - hàm nhận đối số là hàm khác. Đúng rồi, trong FP, function là công dân hạng nhất, nhưng giữa các function với nhau cũng có sự phân biệt đối xử.
Việc định nghĩa các high order function khá khó, nên để tớ làm việc đó cho cậu :smiley:

Tớ sẽ định nghĩa 1 hàm magic nào đó, tên là map - ánh xạ. Hàm này được sử dụng trên List, có tác dụng ánh xạ miền dữ liệu này qua miền dữ liệu khác.
Input: Vì nó là high order function, nó sẽ nhận parameter là hàm f(x) nào đó. Hàm f(x) nhận vào có tác dụng chỉ ra cách cậu ánh xạ như thế nào.
Output: List mới sau khi áp dụng f(x) lên từng phần tử của List cũ.

Trong bài toán của chúng ta, implement sau khi đã có hàm map trên List sẽ như thế này:

var result = {1, 2, 3, 4, 5}.map(x => 2 * x)

x => 2x là 1 hàm, nhận đầu vào là x, và trả về 2 * x. Hàm này sẽ được áp dụng trên từng phần tử của list {1, 2, 3, 4, 5}.
Voila!

Đó là hình thái đơn giản nhất của FP. Tớ đã làm phần khó nhất cho cậu, đó là viết hàm map để áp dụng 1 hàm f(x) lên tất cả các phần tử trong list, và để cho cậu phần dễ hơn, đó là viết ra cách ánh xạ. Hàm map ở đây cũng đủ tổng quát để cậu có thể áp dụng với các bài toán khác, như:

  • Từ danh sách các object Person lấy ra danh sách tên của từng người.
  • Từ danh sách các số thập phân lấy ra danh sách các số nhị phân tương ứng.

Immutable - side effect

Và cậu cũng nên biết rõ, tớ với cậu không chỉnh sửa bất cứ thứ gì trong List cũ ({1, 2, 3, 4, 5} ấy) để ra List mới sau khi dùng map. Tớ với cậu chỉ áp dụng hàm x => 2 * x trên từng phần tử của List cũ, và đưa ra 1 list mới (hay ở ví dụ trên, chúng ta không sửa danh sách object Person, hay không sửa danh sách số thập phân). Điều này rất quan trọng khi cậu phải xây dựng hệ thống các high order functions, và ở đây cậu gọi List đó immutable (không thể chỉnh sửa - tạm dịch).
Tại sao cậu lại phải lo lắng việc chỉnh sửa dữ liệu như vậy? Lý do rất đơn giản, nó sẽ giúp cậu gặp ít lỗi hơn.
Thử tưởng tượng cậu có 1 danh sách chứa fullname của mọi người, và nhiệm vụ của cậu là lấy firstname của từng người. Cậu có 2 lựa chọn:

  1. Cậu sửa danh sách ban đầu, vứt hết lastname và middle name đi
  2. Cậu tạo danh sách mới, loop trên danh sách cũ, tách từng firstname và thêm vào danh sách mới.

Nếu cậu làm theo cách thứ nhất, danh sách của cậu ko còn chứa fullname nữa. Giờ nếu 1 ai đó dùng code của cậu, và dùng list fullname kia, họ sẽ bất ngờ khi nó chỉ chứa toàn firstname thôi! Việc đó gọi là “side effect” - hiệu ứng bên lề.
Đó là 1 nguyên tắc quan trọng trong FP: cậu không sửa đổi dữ liệu cũ -> không có side effect. No error, everybody’s happy!

Tổng kết

Về cơ bản, tớ đã chỉ cho cậu hình dung dùng FP trong thực tế như thế nào. Thường những ngôn ngữ/framework hỗ trợ FP sẽ implement high order function cho cậu, và từ đó cậu sẽ định nghĩa chiến lược phù hợp cho từng bài toán cụ thể.
Giờ tớ sẽ nói về việc FP dùng khi nào, và có thay thế được OOP không. Tớ nghĩ 1 chương trình nên sử dụng cả FP lẫn OOP. 1 cách dễ hiểu nhất cho người mới như cậu, OOP thường được dùng khi cậu muốn tổ chức, mô hình hoá chương trình của cậu. FP thường được dùng nếu cậu quan tâm tới trạng thái của dữ liệu đã thay đổi ra sao qua từng bước (nhớ là chúng ta không sửa đổi dữ liệu).
Rõ ràng, 2 mô thức này có thể được sử dụng cùng nhau trong 1 hệ thống, thế nên tớ nghĩ không có lý do gì để tranh luận cái nào tốt hơn. Tốt nhất là sử dụng cả 2 mô thức một cách hợp lý, để giải quyết vấn đề của cậu.

Hi vọng bài viết này sẽ giúp cậu hiểu được FP sử dụng như thế nào, bonus thêm lý do tại sao cần sử dụng, và sử dụng khi nào. Tớ nghĩ câu trả lời được mark solution hoàn toàn không giải thích được những vấn đề này cho cậu (Opps, câu trả lời của tớ được mark solution rồi. Cảm ơn cậu nhé! :smiley: )

10 Likes
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?