[Collection] Higher Order Functions

Điểm qua một số lý thuyết về collection nào!

Imperative vs. Declarative Programming

Nó không phải là back và white nhé

Imperative Programming

Lập trình theo lệnh hay còn được gọi với tên khác là lập trình hướng thủ tục (Procedural Programming)

Kiểu lập trình này tập trung vào việc “how something gets done“. Code sẽ thường xuyên tập trung vào việc building kết quả trong các biến trung gian và quản lý các luồng thông qua vòng lặp và các điều kiện.

Chúng ta có 1 tập users và chúng ta muốn lấy danh sách email của các users. Code sẽ như sau

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function getUserEmails($users)
{
$emails = [];

for ($i = 0; $i < count($users); $i++) {
$user = $users[$i];

if ($user->email !== null) {
$emails[] = $user->email;
}
}

return $emails;
}

Thay vì cố gắng nói “give me the emails of users who have emails”, việc giải quyết tập trung vào việc thực thi chi tiết vào việc số vòng lặp, truy cập vào chỉ số của cấu trúc dữ liệu và quản lý bộ đếm

Declarative Programming

Thay vì tập trung vào việc máy tính nên làm việc như thế nào thì declaractive programing tập trung vào việc “what we need it to accomplish” (những gì chúng ta cần nó để đạt được mục đích.)

So sánh đoạn code trên với đoạn code trong SQL:

1
SELECT email FROM users WHERE email IS NOT NULL

Chúng ta không phải viết bất cứ gì về vòng lặp, bộ đếm hay array indexs. Chúng ta chỉ nói với máy tính cái chúng ta muốn và không quan tâm nó lấy dữ liệu như thế nào.

Under the hood, I’m sure the SQL engine must be doing some sort of iteration or keeping track of which records it’s checked or which records it hasn’t, but I don’t really know for sure. And that’s the beauty of it: I don’t need to know.

Và dĩ nhiên PHP và một ngôn ngữ khác SQL và chúng ta sẽ không thể tạo lại cú pháp chính xác đó. Tuy nhiên chúng ta có thể tạo 1 cái gì đó gần giống? Chắc chắn là được, sử dụng Higher Order Functions

Higher Order Functions

Tạm dịch là hàm bậc cao đi :D

A higher order function is a function that takes another function as a parameter, returns a function, or does both.
(Một higher order function là 1 hàm nhận 1 hàm khác như 1 tham số, trả về 1 hàm hoặc làm cả 2)

Xem xét ví dụ sau, hàm bọc code database transaction:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public function transaction($func)
{
$this->beginTransaction();

try {
$result = $func();
$this->commitTransaction();
} catch (Exception $e) {
$this->rollbackTransaction();
throw $e;
}

return $result
}

Và đây là cách chúng ta sử dụng:
1
2
3
4
5
6
7
try {
$databaseConnection->transaction(function () use ($comment) {
$comment->save();
});
} catch (Exception $e) {
echo "Something went wrong!";
}

Noticing Patterns

HOF có sức mạnh to lớn vì chúng cho phép chúng ta tạo sự trừu tượng xung quanh các mẫu lập trình phổ biến mà không thể sử dụng lại

Trở lại bài toán lấy danh ách email của khách hàng:

1
2
3
4
5
6
7
$customerEmails = [];

foreach ($customers as $customer) {
$customerEmails[] = $customer->email;
}

return $customerEmails;

Và bây giờ chúng ta cũng có danh sách các sản phẩm hàng tồn kho và chúng ta muốn biết tổng giá trị dư thừa của chúng ta là bao nhiêu:

1
2
3
4
5
6
7
8
9
10
$stockTotals = [];

foreach ($inventoryItems as $item) {
$stockTotals[] = [
'product' => $item->productName,
'total_value' => $item->quantity * $item->price,
];
}

return $stockTotals;

Nhìn lướt qua thì nó không có gì là quá trừu tượng hóa (abstract) được ở đây nhưng nếu bạn nhìn kỹ lại bạn sẽ nhận thấy chỉ có 1 điểm khác nhau thực sự giữa 2 ví dụ này.

Trong cả 2 ví dụ, tất cả cái chúng ta làm là tạo ra 1 mảng mới bằng cách áp dụng 1 vài toán tử với mỗi item trong danh sách đã tồn tại. Cái mà chỉ khác nhau giữa 2 vi dụ thực chất là toán tử mà ta áp dụng

Trong ví dụ đầu tiên, chúng ta chỉ thêm trường email vào mỗi item

Trong ví dụ thứ 2, chúng ta tạo ra một tập hợp mảng từ 1 vài trường của item.

Như vậy chúng ta đã thấy được sự trừu tượng có thể áp dụng ở đây, đó là việc truyền vào hàm xử lý cho mỗi xử lý là được

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function map($items, $func)
{
$results = [];

foreach ($items as $item) {
$results[] = $func($item);
}

return $results;
}

$customerEmails = map($customers, function ($customer) {
return $customer->email;
});

$stockTotals = map($inventoryItems, function ($item) {
return [
'product' => $item->productName,
'total_value' => $item->quantity * $item->price,
];
});
Author

Ming

Posted on

2019-12-27

Updated on

2021-04-10

Licensed under

Comments