Lập trình chức năng là hành trình mở rộng tầm mắt đối với tôi. Bài đăng này và các bài đăng tương tự là một nỗ lực để chia sẻ những hiểu biết và quan điểm của tôi khi tôi khám phá những vùng đất lập trình chức năng mới.
Ramda là thư viện FP mà tôi tìm đến vì nó giúp lập trình chức năng trong JavaScript dễ dàng hơn bao nhiêu. Tôi khuyên bạn nên nó.
Đường ống
Khái niệm của pipe
thật đơn giản – nó kết hợp n
chức năng. Đó là một đường ống chảy từ trái sang phải, gọi từng chức năng bằng đầu ra của chức năng cuối cùng.
Hãy viết một hàm trả về của ai đó name
.
getName = (person) => person.name;
getName({ name: 'Buckethead' });
// 'Buckethead'
Hãy viết một hàm viết hoa các chuỗi.
uppercase = (string) => string.toUpperCase();
uppercase('Buckethead');
// 'BUCKETHEAD'
Vì vậy, nếu chúng ta muốn nhận và viết hoa person
‘s name, chúng ta có thể làm điều này:
name = getName({ name: 'Buckethead' });
uppercase(name);
// 'BUCKETHEAD'
Điều đó tốt nhưng hãy loại bỏ biến trung gian đó name
.
uppercase(getName({ name: 'Buckethead' }));
Tốt hơn, nhưng tôi không thích làm tổ đó. Nó có thể trở nên quá đông đúc. Nếu chúng ta muốn thêm một hàm nhận 6 ký tự đầu tiên của chuỗi thì sao?
get6Characters = (string) => string.substring(0, 6);
get6Characters('Buckethead');
// 'Bucket'
Kết quả là:
get6Characters(uppercase(getName({ name: 'Buckethead' })));
// 'BUCKET';
Hãy thực sự điên cuồng và thêm một chức năng để đảo ngược chuỗi.
reverse = (string) =>
string
.split('')
.reverse()
.join('');
reverse('Buckethead');
// 'daehtekcuB'
Bây giờ chúng tôi có:
reverse(get6Characters(uppercase(getName({ name: 'Buckethead' }))));
// 'TEKCUB'
Nó có thể nhận được một chút… nhiều.
Ống để giải cứu!
Thay vì gây nhiễu các hàm trong các hàm hoặc tạo một loạt các biến trung gian, hãy pipe
tất cả mọi thứ!
pipe(
getName,
uppercase,
get6Characters,
reverse
)({ name: 'Buckethead' });
// 'TEKCUB'
Nghệ thuật thuần khiết. Nó giống như một danh sách việc cần làm!
Hãy bước qua nó.
Đối với mục đích demo, tôi sẽ sử dụng một pipe
triển khai từ một trong những bài viết về lập trình chức năng của Eric Elliott.
pipe = (...fns) => (x) => fns.reduce((v, f) => f(v), x);
Tôi yêu chiếc áo lót nhỏ này.
sử dụng còn lại tham số, hãy xem bài viết của tôi về điều đó, chúng ta có thể dẫn n
chức năng. Mỗi chức năng lấy đầu ra của chức năng trước đó và đó là tất cả giảm ? đến một giá trị duy nhất.
Và bạn có thể sử dụng nó giống như chúng tôi đã làm ở trên.
pipe(
getName,
uppercase,
get6Characters,
reverse
)({ name: 'Buckethead' });
// 'TEKCUB'
tôi sẽ mở rộng pipe
và thêm một số câu lệnh trình gỡ lỗi và chúng tôi sẽ đi từng dòng một.
pipe = (...functions) => (value) => {
debugger;
return functions.reduce((currentValue, currentFunction) => {
debugger;
return currentFunction(currentValue);
}, value);
};
Cuộc gọi pipe
với ví dụ của chúng tôi và để cho những điều kỳ diệu mở ra.
Kiểm tra các biến cục bộ. functions
là một mảng của 4 chức năng, và value
Là { name: 'Buckethead' }
.
kể từ khi chúng tôi sử dụng còn lại thông số, pipe
cho phép bất kỳ số lượng chức năng nào được sử dụng. Nó sẽ chỉ lặp lại và gọi từng cái một.
Trên trình gỡ lỗi tiếp theo, chúng tôi đang ở trong reduce
. Đây là đâu currentValue
được chuyển đến currentFunction
và đã trở lại.
Ta thấy kết quả là 'Buckethead'
bởi vì currentFunction
trả về .name
thuộc tính của bất kỳ đối tượng nào. Điều đó sẽ được trả lại trong reduce
có nghĩa là nó trở thành cái mới currentValue
lần tới. Hãy nhấn trình gỡ lỗi tiếp theo và xem.
Hiện nay currentValue
Là ‘Buckethead’
bởi vì đó là những gì đã được trả lại lần trước. currentFunction
Là uppercase
Vì thế 'BUCKETHEAD'
sẽ là người tiếp theo currentValue
.
Cùng một ý tưởng, nhổ ‘BUCKETHEAD’
của 6 ký tự đầu tiên và chuyển chúng sang chức năng tiếp theo.
reverse(‘.aedi emaS’)
Và bạn đã hoàn tất!
Còn soạn thư() thì sao?
Nó chỉ pipe
theo hướng khác.
Vì vậy, nếu bạn muốn kết quả tương tự như của chúng tôi pipe
ở trên, bạn sẽ làm ngược lại.
compose(
reverse,
get6Characters,
uppercase,
getName
)({ name: 'Buckethead' });
Nhận thấy như thế nào getName
là cuối cùng trong chuỗi và reverse
Là đầu tiên?
Đây là một thực hiện nhanh chóng của compose
một lần nữa với sự giúp đỡ của Magical Eric Elliott, từ cùng một bài báo.
compose = (...fns) => (x) => fns.reduceRight((v, f) => f(v), x);
Tôi sẽ tiếp tục mở rộng chức năng này với debugger
s như một bài tập cho bạn. Chơi với nó, sử dụng nó, đánh giá cao nó. Và điều quan trọng nhất là phải vui!