Trở thành Functional Programmer - Phần 2
Đây là bài dịch từ bài gốc ở link sau : https://medium.com/@cscalfani/so-you-want-to-be-a-functional-programmer-part-2-7005682cec4a#.eqo0af4ak Những bước đầu tiên của việc hiểu rõ các concepts trong lập trình hàm (Functional Programming - FP) là những bước quan trọng nhất, và đôi khi là những ...
Đây là bài dịch từ bài gốc ở link sau : https://medium.com/@cscalfani/so-you-want-to-be-a-functional-programmer-part-2-7005682cec4a#.eqo0af4ak
Những bước đầu tiên của việc hiểu rõ các concepts trong lập trình hàm (Functional Programming - FP) là những bước quan trọng nhất, và đôi khi là những bước khó khăn nhất. Nhưng với cách tiếp cận đúng đắn, mọi thứ sẽ trở nên dễ hiểu hơn rất nhiều. Và đây là series được tạo ra nhằm mục đích giúp các bạn dễ thở hơn trong quá trình tiếp cận với FP.
Link của phần trước: Phần 1
Một chút lưu ý
Tôi mong các bạn sẽ đọc các dòng code một cách từ tốn. Và hãy đảm bảo rằng bạn đã hoàn toàn hiểu rõ, nắm vững các nội dung vừa đọc được trước khi tiếp tục. Các phần tiếp theo được phát triển từ các phần trước đó, nên nếu bạn vội vã, bạn có thể bỏ qua một vài kiến thức quan trọng, cần thiết cho các phần sau này.
Tái cấu trúc - Refactoring
Phần này sẽ nói về Tái cấu trúc - một kỹ thuật khá quen thuộc đối với các lập trình viên. Sau đây mời các bạn xem một đoạn code Javascript:
function validateSsn(ssn) { if (/^d{3}-d{2}-d{4}**/.exec(**ssn**))         console.log('Valid **SSN**');     else         console.log('Invalid **SSN**'); } function validatePhone(**phone**) {     if (/**^(d{3})d{3}-d{4}/.exec(phone)) console.log('Valid Phone Number'); else console.log('Invalid Phone Number'); }
Hẳn là bạn đã từng viết những dòng code kiểu như thế này, và theo thời gian, bạn sẽ dần nhận ra rằng 2 hàm phía trên khá là giống nhau, chỉ có đôi chút khác biệt (phần được bôi đậm).
Vì thế, thay vì copy lại hàm validateSSn và thay đổi để tạo ra hàm validatePhone mới, chúng ta có thể chỉ tạo một hàm và biến các phần khác nhau thành tham số.
Trong ví dụ này, chúng ta nên tham số hóa phần value, phần regulare expression và phần message được in ra (là những phần bôi đậm ở trên).
Đây là code sau khi refactor:
function validateValue(value, regex, type) { if (regex.exec(value)) console.log('Invalid ' + type); else console.log('Valid ' + type); }
Các tham số ssn và phone ở trong phần code cũ đã được thay thế bằng biến value.
2 biểu thức chính quy (regulare expression) /^d{3}-d{2}-d{4}$/ và /^(d{3})d{3}-d{4}$/ được thay thế bằng biến regex.
Và phần sau của message gồm SNS và Phone Number sẽ được thay thế bằng biến type.
Việc chỉ có một hàm như thế này sẽ tốt hơn rất nhiều so với việc có 2, hoặc xấu hơn là 3, 4 hay 10 hàm. Việc này sẽ giúp code của bạn sạch sẽ và dễ bảo trì hơn.
Ví dụ, nếu có lỗi xảy ra, bạn sẽ chỉ phải fix ở một chỗ thay vì tìm kiếm trong tất cả source code để tìm các chỗ mà hàm này CÓ THỂ đã được copy/paste và thay đổi.
Tiếp theo chúng ta cùng xem xét trường hợp phức tạp hơn một chút :
function validateAddress(address) { if (parseAddress(address)) console.log('Valid Address'); else console.log('Invalid Address'); } function validateName(name) { if (parseFullName(name)) console.log('Valid Name'); else console.log('Invalid Name'); }
Ở đây parseAddress và parseFullName là 2 hàm đều nhận vào một chuỗi và trả về true nếu parse thành công.
Bạn sẽ refactor code trong trường hợp này như thế nào đây?
Giống như trường hợp trước đó, ta có thể sử dụng biến value cho address và name, type cho Address và Name giống như đã làm trước đó, nhưng ở vị trí của biểu thức chính quy lúc trước giờ lại là 2 hàm khác nhau.
Nếu như chúng ta có thể đưa hàm vào tham số thì ...
Concept 3: Higher-Order Functions
(Chú thích của người dịch: High-order Function mình đã tìm hiểu nhưng khó có từ tiếng Việt tương đương, bạn có thể hiểu Higher-Order Functions có nghĩa là Hàm có cấp bậc cao hơn - với ý nghĩa là hàm có nhiều khả năng và linh hoạt hơn so với các ngôn ngữ Imperative Programming thông thường như Java, C, C++)
Rất nhiều ngôn ngữ lập trình không hỗ trợ việc đưa hàm vào thành tham số. Một số ngôn ngữ thì có thể nhưng cách thực hiện thì không hề dễ dàng chút nào.
Trong Functional Programming, một hàm sẽ được coi như là một công dân hạng nhất trong ngôn ngữ đó. Hay nói cách khác, hàm sẽ giống như các loại giá trị (số, text, object,...) khác.
Bởi vì hàm sẽ được coi như các loại giá trị khác, nên hàm có thể được truyền dưới dạng tham số.
Mặc dù không phải là ngôn ngữ hỗ trợ FP chính thống, nhưng một vài concept trong FP có thể được thực hiện bởi Javascript. Và đây là cách thu gọn hai hàm ở trên thành một bằng việc đưa hàm thực hiện việc parse dữ liệu thành tham số của hàm mới có tên là parseFunc:
function validateValueWithFunc(value, parseFunc, type) { if (parseFunc(value)) console.log('Invalid ' + type); else console.log('Valid ' + type); }
Và hàm mới của chúng ta được gọi là Higher-order Function.
Higher-order Functions là các hàm hoặc nhận hàm làm tham số, hoặc trả về hàm, hoặc vừa nhận hàm làm tham số vừa trả về hàm.
Và giờ chúng ta có thể viết lại cả 4 hàm trên bằng cách sử dụng hàm validateValueWithFunc như sau ( lưu ý rằng hàm Regex.exec sẽ trả về true nếu chuỗi ký tự match với biểu thức chính quy ):
validateValueWithFunc('123-45-6789', /^d{3}-d{2}-d{4}$/.exec, 'SSN'); validateValueWithFunc('(123)456-7890', /^(d{3})d{3}-d{4}$/.exec, 'Phone'); validateValueWithFunc('123 Main St.', parseAddress, 'Address'); validateValueWithFunc('Joe Mama', parseName, 'Name');
Code được viết lại như trên nhìn ngon hơn hẳn so với việc có 4 hàm từa tựa nhau nhỉ?