Đã có rất nhiều câu trả lời tuyệt vời cho câu hỏi này trên Internet. Tôi sẽ viết một bản tổng hợp một số giải thích và ví dụ tôi đã thu thập về chủ đề này, chỉ trong trường hợp ai đó có thể thấy nó hữu ích
GIỚI THIỆU
gọi theo giá trị (CBV)
Thông thường, tham số cho các chức năng là tham số gọi theo giá trị; nghĩa là, các tham số được ước tính từ trái sang phải để xác định giá trị của chúng trước khi chính hàm được ước tính
def first(a: Int, b: Int): Int = a
first(3 + 4, 5 + 6) // will be reduced to first(7, 5 + 6), then first(7, 11), and then 7
gọi bằng tên (CBN)
Nhưng điều gì sẽ xảy ra nếu chúng ta cần viết một hàm chấp nhận như một tham số một biểu thức mà chúng ta không đánh giá cho đến khi nó được gọi trong hàm của chúng ta? Đối với trường hợp này, Scala cung cấp các tham số gọi theo tên. Có nghĩa là tham số được truyền vào hàm như hiện tại và việc định giá của nó diễn ra sau khi thay thế
def first1(a: Int, b: => Int): Int = a
first1(3 + 4, 5 + 6) // will be reduced to (3 + 4) and then to 7
Một cơ chế gọi bằng tên chuyển một khối mã cho cuộc gọi và mỗi lần cuộc gọi truy cập tham số, khối mã được thực thi và giá trị được tính. Trong ví dụ sau, trì hoãn in một thông báo chứng minh rằng phương thức đã được nhập. Tiếp theo, trì hoãn in một tin nhắn với giá trị của nó. Cuối cùng, trả lại chậm 't':
object Demo {
def main(args: Array[String]) {
delayed(time());
}
def time() = {
println("Getting time in nano seconds")
System.nanoTime
}
def delayed( t: => Long ) = {
println("In delayed method")
println("Param: " + t)
}
}
Trong phương pháp trì hoãn
Nhận thời gian tính bằng nano giây Thông
số: 2027245119786400
PROS VÀ TIÊU DÙNG CHO MACHI TRƯỜNG HỢP
CBN:
+ Chấm dứt thường xuyên hơn * kiểm tra bên dưới chấm dứt * + Có lợi thế là không đánh giá được đối số hàm nếu tham số tương ứng không được sử dụng trong đánh giá thân hàm - Nó chậm hơn, nó tạo ra nhiều lớp hơn (nghĩa là chương trình mất lâu hơn để tải) và nó tiêu thụ nhiều bộ nhớ hơn.
CBV:
+ Nó thường hiệu quả hơn theo cấp số nhân so với CBN, vì nó tránh sự tính toán lặp lại này của các biểu thức đối số gọi bằng tên. Nó đánh giá mọi đối số chức năng chỉ một lần + Nó chơi đẹp hơn nhiều với các hiệu ứng bắt buộc và tác dụng phụ, bởi vì bạn có xu hướng biết rõ hơn khi các biểu thức sẽ được đánh giá. -Có thể dẫn đến một vòng lặp trong quá trình đánh giá tham số của nó * kiểm tra bên dưới chấm dứt *
Nếu chấm dứt không được đảm bảo thì sao?
-Nếu đánh giá CBV của một biểu thức e chấm dứt, thì đánh giá CBN của e cũng chấm dứt -Điều khác là không đúng
Ví dụ không chấm dứt
def first(x:Int, y:Int)=x
Hãy xem xét biểu thức đầu tiên (1, vòng lặp)
CBN: đầu tiên (1, vòng lặp) → 1 CBV: đầu tiên (1, vòng lặp) → giảm các đối số của biểu thức này. Vì một là một vòng lặp, nó làm giảm các đối số một cách vô tận. Nó không chấm dứt
NHỮNG KHÁC BIỆT TRONG MACHI TRƯỜNG HỢP
Hãy xác định một thử nghiệm phương pháp sẽ là
Def test(x:Int, y:Int) = x * x //for call-by-value
Def test(x: => Int, y: => Int) = x * x //for call-by-name
Kiểm tra Case1 (2,3)
test(2,3) → 2*2 → 4
Vì chúng tôi bắt đầu với các đối số đã được đánh giá, nó sẽ có cùng số lượng bước cho cuộc gọi theo giá trị và cuộc gọi theo tên
Thử nghiệm Case2 (3 + 4,8)
call-by-value: test(3+4,8) → test(7,8) → 7 * 7 → 49
call-by-name: (3+4)*(3+4) → 7 * (3+4) → 7 * 7 → 49
Trong trường hợp này, gọi theo giá trị thực hiện ít bước hơn
Thử nghiệm Case3 (7, 2 * 4)
call-by-value: test(7, 2*4) → test(7,8) → 7 * 7 → 49
call-by-name: (7)*(7) → 49
Chúng tôi tránh tính toán không cần thiết của đối số thứ hai
Kiểm tra Case4 (3 + 4, 2 * 4)
call-by-value: test(7, 2*4) → test(7,8) → 7 * 7 → 49
call-by-name: (3+4)*(3+4) → 7*(3+4) → 7*7 → 49
Phương pháp khác nhau
Đầu tiên, giả sử chúng ta có một chức năng với tác dụng phụ. Hàm này in ra một cái gì đó và sau đó trả về một Int.
def something() = {
println("calling something")
1 // return value
}
Bây giờ chúng ta sẽ định nghĩa hai hàm chấp nhận các đối số Int hoàn toàn giống nhau ngoại trừ một hàm lấy đối số theo kiểu gọi theo giá trị (x: Int) và hàm kia theo kiểu gọi theo tên (x: => Int).
def callByValue(x: Int) = {
println("x1=" + x)
println("x2=" + x)
}
def callByName(x: => Int) = {
println("x1=" + x)
println("x2=" + x)
}
Bây giờ điều gì xảy ra khi chúng ta gọi chúng với chức năng tác dụng phụ?
scala> callByValue(something())
calling something
x1=1
x2=1
scala> callByName(something())
calling something
x1=1
calling something
x2=1
Vì vậy, bạn có thể thấy rằng trong phiên bản gọi theo giá trị, tác dụng phụ của lệnh gọi hàm truyền vào (một cái gì đó ()) chỉ xảy ra một lần. Tuy nhiên, trong phiên bản gọi bằng tên, hiệu ứng phụ đã xảy ra hai lần.
Điều này là do các hàm gọi theo giá trị tính toán giá trị của biểu thức được truyền trước khi gọi hàm, do đó, cùng một giá trị được truy cập mỗi lần. Tuy nhiên, các hàm gọi theo tên sẽ tính toán lại giá trị của biểu thức truyền vào mỗi khi nó được truy cập.
VÍ DỤ Ở ĐÂU LÀ TỐT HƠN ĐỂ SỬ DỤNG CUỘC GỌI
Từ: https://stackoverflow.com/a/19036068/1773841
Ví dụ hiệu suất đơn giản: đăng nhập.
Hãy tưởng tượng một giao diện như thế này:
trait Logger {
def info(msg: => String)
def warn(msg: => String)
def error(msg: => String)
}
Và sau đó được sử dụng như thế này:
logger.info("Time spent on X: " + computeTimeSpent)
Nếu phương thức thông tin không làm gì cả (vì giả sử, mức ghi nhật ký được định cấu hình cao hơn mức đó), thì computeTimeSpent không bao giờ được gọi, tiết kiệm thời gian. Điều này xảy ra rất nhiều với loggers, trong đó người ta thường thấy thao tác chuỗi có thể tốn kém so với các tác vụ được ghi.
Ví dụ đúng: toán tử logic.
Bạn có thể đã thấy mã như thế này:
if (ref != null && ref.isSomething)
Hãy tưởng tượng bạn sẽ khai báo && phương thức như thế này:
trait Boolean {
def &&(other: Boolean): Boolean
}
sau đó, bất cứ khi nào ref là null, bạn sẽ gặp lỗi vì isS Something sẽ được gọi bằng nullreference trước khi được chuyển đến &&. Vì lý do này, tuyên bố thực tế là:
trait Boolean {
def &&(other: => Boolean): Boolean =
if (this) this else other
}
=> Int
là một loại khác vớiInt
; đó là "chức năng không có đối số sẽ tạo raInt
" so với chỉInt
. Khi bạn đã có các hàm hạng nhất, bạn không cần phải phát minh thuật ngữ gọi theo tên để mô tả điều này.