Đâ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 foo
như 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, return
là ra khỏi vị trí; foo
trô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 bar
sẽ ít rõ ràng hơn và vô nghĩa khi bạn có return
. Điều làm cho bar
rất dễ để return
tuyê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 bar
theo 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 while
vò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 return
là không đáng kể, nhưng hiệu quả đạt được bằng cách tránh return
và 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 return
có 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 return
là 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 return
câ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ề TRUE
nếu tất cả các giá trị trong một giá trị x
có độ 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 ?ifelse
chắ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ế, ifelse
có 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 foo
bằng mộtifelse
foo = function (a) ifelse(a,a,b)
Bây giờ foo
hoạ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 a
là một ma trận. Trả lại một giá trị có hình dạng giống như test
một tính năng giúp vector hóa, không phải là một vấn đề.
return
là không cần thiết ngay cả trong ví dụ cuối cùng. Việc xóareturn
có 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.