Có thể bạn vừa nghe về lập trình hàm và có một số câu hỏi.
Như…
- Lập trình chức năng chính xác là gì?
- So sánh với lập trình hướng đối tượng như thế nào?
- Bạn có nên sử dụng lập trình hàm trong Ruby không?
Hãy để tôi trả lời những câu hỏi này cho bạn để bạn có thể hiểu rõ hơn về cách hoạt động của tính năng này.
Lập trình chức năng là gì?
Đó không chỉ là một thứ mốt nhất thời hay một từ hoa mỹ, đó là một mô hình lập trình thực tế đã có từ lâu nhưng gần đây nó đã trở lại phổ biến.
Và những ý tưởng cơ bản đằng sau mô hình này dễ hiểu hơn bạn nghĩ.
Trong lập trình chức năng, chúng tôi tránh thay đổi trạng thái &chúng tôi cố gắng viết các hàm “thuần túy” .
Tránh thay đổi trạng thái có nghĩa là các hàm này không thay đổi bất kỳ điều gì bên ngoài hàm, không có biến phiên bản, không thay đổi một số đối tượng đã được chuyển vào…
Không có điều đó!
Trong một ngôn ngữ lập trình chức năng (như Haskell), tất cả dữ liệu đều CÓ THỂ NGAY LẬP TỨC.
Có những thứ giống như các biến, nhưng chúng hoạt động giống như trong thế giới toán học hơn. Khi một biến được cung cấp một giá trị, trình biên dịch sẽ không cho phép bạn xác định lại biến này bằng một giá trị khác.
Lợi ích của lập trình chức năng
Tính bất biến là ưu điểm chính của lập trình chức năng vì dữ liệu có thể thay đổi có thể dẫn đến các lỗi nhỏ khó theo dõi.
Ví dụ :
def all_different_from_first?(arr) first = arr.shift arr.all? { |n| n != first } end arr = [1,3,5,7,9] p all_different_from_first?(arr) # true
Trong ví dụ này, tôi muốn tìm hiểu xem tất cả các phần tử trong mảng có khác với phần tử đầu tiên hay không.
Để thực hiện công việc này, chúng tôi cần xóa phần tử đầu tiên khỏi mảng và đồng thời lưu phần tử này để chúng tôi có thể so sánh nó với phần còn lại.
Làm thế nào chúng tôi có thể làm điều đó?
Chúng tôi đang làm việc với một mảng và nếu bạn xem danh sách các phương thức có sẵn, bạn sẽ thấy rằng phương thức Array # shift thực hiện chính xác những gì chúng tôi muốn.
Nó hoạt động tốt cho đến khi…
… Bạn nhìn vào giá trị của arr
sau khi gọi phương thức một lần:
all_different_from_first?(arr) # true arr # [3,5,7,9]
Thật bất ngờ!
Mảng bị mất một phần tử (1
) và chúng tôi không nhận thấy.
Đó là cách loại lỗi đột biến này có thể lén lút.
Phiên bản cố định :
def all_different_from_first?(arr) arr[1..-1].all? { |n| n != arr.first } end
Chức năng so với OOP
Tất cả chúng ta có nên chỉ áp dụng lập trình chức năng không?
Có vẻ như tất cả trạng thái bất biến này làm cho lập trình chức năng hoàn toàn trái ngược với OOP và theo một nghĩa nào đó, nhưng vẫn có một cách để hai mô hình lập trình có thể hoạt động cùng nhau .
Vì vậy, không, không cần phải vội vàng và bắt đầu lập trình đầy đủ chức năng. Dù sao thì Ruby cũng được thiết kế cho OOP, vì vậy bạn sẽ phải chiến đấu chống lại vấn đề này.
Tin tốt :
Bạn vẫn có thể sử dụng những ý tưởng tốt nhất từ lập trình chức năng và áp dụng chúng vào mã Ruby của mình.
Hãy nói về cách thực hiện điều đó.
Giảm thiểu khả năng gây đột biến càng nhiều càng tốt
Một cách để làm điều đó là DỪNG sử dụng attr_accessor
, chỉ dính vào attr_reader
.
Sau khi làm điều đó, bạn cần theo dõi các chuỗi, mảng và hàm băm.
Có những phương pháp sẽ thay đổi những đối tượng này:
- Hầu hết các phương thức kết thúc bằng
!
(nhưgsub!
) - xóa
- cập nhật
- rõ ràng
- shift / unshift / pop / push
Bước đầu tiên là nhận thức về các phương pháp này.
Nếu bạn phải sử dụng một trong những phương pháp này, bạn có thể làm việc trên một đối tượng trùng lặp.
Đã cho một chuỗi và một bản sao của chuỗi đó :
str = "abcd" dup = str.dup
Chúng tôi nhận được những kết quả này khi chúng tôi clear
chuỗi trùng lặp:
dup.clear # str => "abcd" # dup => ""
Điều này giữ cho chuỗi gốc an toàn.
Đơn đăng ký một phần
Lập trình chức năng có nhiều thứ hơn là dữ liệu bất biến và các hàm thuần túy.
Giống như ứng dụng một phần của các chức năng, còn được gọi là “currying”.
Ví dụ :
def add(a,b) a + b end add_five = method(:add).curry[5] add_five.call(5) # 10 add_five.call(20) # 25
Lưu ý cách add
phương thức nhận hai đối số, nhưng bằng cách sử dụng curry
chúng ta có thể "tải trước" một trong các đối số.
Sau đó, chúng ta nhận được một lambda mà chúng ta có thể gọi chỉ với đối số thứ 2.
Đây là một ví dụ khác :
list = (1..10) greater_than = ->(x,y) { y > x }.curry list.select(&greater_than.(5)) # [6, 7, 8, 9, 10] list.select(&greater_than.(8)) # [9, 10]
Một ví dụ khác :
divisible_by = ->(x,y) { y % x == 0 }.curry list.select(&divisible_by.(5)) # [5, 10] list.select(&divisible_by.(2)) # [2, 4, 6, 8, 10]
Tóm tắt
Bạn đã học về lập trình hàm, cốt lõi của nó là các hàm thuần túy và dữ liệu bất biến, đó chỉ là một cách để suy nghĩ về mã của bạn và không hoàn toàn không tương thích với OOP.
Cảm ơn bạn đã đọc, đừng quên đăng ký nhận bản tin nếu bạn chưa đọc! 🙂