Đây là một cuộc thảo luận thú vị. Tôi nghĩ rằng ví dụ của @ flodel là tuyệt vời. Tuy nhiên, tôi nghĩ rằng nó minh họa quan điểm của tôi (và @koshke đề cập đến điều này trong một bình luận) có returný nghĩa khi bạn sử dụng mệnh lệnh thay vì kiểu mã hóa chức năng .
Không tin vào quan điểm, nhưng tôi sẽ viết lại foonhư thế này:
foo = function() ifelse(a,a,b)
Một kiểu chức năng tránh các thay đổi trạng thái, như lưu trữ giá trị của output. Trong phong cách này, returnlà ra khỏi vị trí; footrông giống như một hàm toán học.
Tôi đồng ý với @flodel: sử dụng một hệ thống phức tạp của các biến boolean trong barsẽ ít rõ ràng hơn và vô nghĩa khi bạn có return. Điều làm cho barrất dễ để returntuyên bố là nó được viết theo phong cách bắt buộc. Thật vậy, các biến boolean đại diện cho các thay đổi "trạng thái" được tránh trong một kiểu chức năng.
Thật sự rất khó để viết lại bartheo phong cách chức năng, bởi vì nó chỉ là mã giả, nhưng ý tưởng là như thế này:
e_func <- function() do_stuff
d_func <- function() ifelse(any(sapply(seq(d),e_func)),2,3)
b_func <- function() {
do_stuff
ifelse(c,1,sapply(seq(b),d_func))
}
bar <- function () {
do_stuff
sapply(seq(a),b_func) # Not exactly correct, but illustrates the idea.
}
Các whilevòng lặp sẽ là khó khăn nhất để viết lại, bởi vì nó được điều khiển bởi những thay đổi trạng thái để a.
Mất tốc độ gây ra bởi một cuộc gọi đến returnlà không đáng kể, nhưng hiệu quả đạt được bằng cách tránh returnvà viết lại theo kiểu chức năng thường là rất lớn. Nói người dùng mới ngừng sử dụng returncó thể sẽ không giúp ích gì, nhưng hướng dẫn họ theo phong cách chức năng sẽ được đền đáp.
@Paul returnlà cần thiết theo kiểu bắt buộc vì bạn thường muốn thoát hàm tại các điểm khác nhau trong một vòng lặp. Một kiểu chức năng không sử dụng các vòng lặp, và do đó không cần return. Trong một phong cách chức năng thuần túy, cuộc gọi cuối cùng hầu như luôn luôn là giá trị trả về mong muốn.
Trong Python, các hàm yêu cầu một returncâu lệnh. Tuy nhiên, nếu bạn lập trình chức năng của mình theo kiểu chức năng, bạn có thể sẽ chỉ có mộtreturn câu lệnh: ở cuối chức năng của bạn.
Sử dụng một ví dụ từ một bài đăng StackOverflow khác, giả sử chúng tôi muốn một hàm trả về TRUEnếu tất cả các giá trị trong một giá trị xcó độ dài lẻ. Chúng tôi có thể sử dụng hai phong cách:
# Procedural / Imperative
allOdd = function(x) {
for (i in x) if (length(i) %% 2 == 0) return (FALSE)
return (TRUE)
}
# Functional
allOdd = function(x)
all(length(x) %% 2 == 1)
Trong một kiểu chức năng, giá trị được trả về tự nhiên rơi vào cuối của hàm. Một lần nữa, nó trông giống như một hàm toán học.
@GSee Các cảnh báo được nêu trong ?ifelsechắc chắn rất thú vị, nhưng tôi không nghĩ họ đang cố gắng từ chối sử dụng chức năng này. Trong thực tế, ifelsecó lợi thế của các chức năng vector hóa tự động. Ví dụ: hãy xem xét một phiên bản sửa đổi một chút của foo:
foo = function(a) { # Note that it now has an argument
if(a) {
return(a)
} else {
return(b)
}
}
Hàm này hoạt động tốt khi length(a)là 1. Nhưng nếu bạn viết lại foobằng mộtifelse
foo = function (a) ifelse(a,a,b)
Bây giờ foohoạt động trên bất kỳ chiều dài của a. Trong thực tế, nó thậm chí sẽ hoạt động khi alà một ma trận. Trả lại một giá trị có hình dạng giống như testmột tính năng giúp vector hóa, không phải là một vấn đề.
returnlà không cần thiết ngay cả trong ví dụ cuối cùng. Việc xóareturncó thể làm cho nó nhanh hơn một chút, nhưng theo quan điểm của tôi thì điều này là do R được cho là một ngôn ngữ lập trình thích hợp.