Sự khác biệt về ngữ nghĩa đã được giải thích khá rõ trong câu trả lời được liên kết bởi Plasty Grove .
Về mặt chức năng, dường như không có nhiều sự khác biệt. Hãy xem một số ví dụ để xác minh điều đó. Đầu tiên, một chức năng bình thường:
scala> def modN(n: Int, x: Int): Boolean = ((x % n) == 0)
scala> modN(5, _ : Int)
res0: Int => Boolean = <function1>
Vì vậy, chúng tôi nhận được áp dụng một phần <function1>
nhận một Int
, bởi vì chúng tôi đã cung cấp cho nó số nguyên đầu tiên. Càng xa càng tốt. Bây giờ đến món cà ri:
scala> def modNCurried(n: Int)(x: Int): Boolean = ((x % n) == 0)
Với ký hiệu này, bạn sẽ ngây thơ mong đợi những điều sau sẽ hoạt động:
scala> modNCurried(5)
<console>:9: error: missing arguments for method modN;
follow this method with `_' if you want to treat it as a partially applied function
modNCurried(5)
Vì vậy, ký hiệu danh sách nhiều tham số dường như không thực sự tạo ra một hàm curry ngay lập tức (giả sử là để tránh chi phí không cần thiết) mà đợi bạn tuyên bố rõ ràng rằng bạn muốn nó được thực hiện (ký hiệu này cũng có một số lợi thế khác ):
scala> modNCurried(5) _
res24: Int => Boolean = <function1>
Đó chính xác là thứ chúng ta đã có trước đây, vì vậy không có sự khác biệt ở đây, ngoại trừ ký hiệu. Một vi dụ khac:
scala> modN _
res35: (Int, Int) => Boolean = <function2>
scala> modNCurried _
res36: Int => (Int => Boolean) = <function1>
Điều này cho thấy cách áp dụng một phần hàm "bình thường" dẫn đến một hàm nhận tất cả các tham số, trong khi việc áp dụng một phần một hàm với nhiều danh sách tham số sẽ tạo ra một chuỗi hàm, mỗi hàm trên một danh sách tham số , tất cả đều trả về một hàm mới:
scala> def foo(a:Int, b:Int)(x:Int)(y:Int): Int = a * b + x - y
scala> foo _
res42: (Int, Int) => Int => (Int => Int) = <function2>
scala> res42(5)
<console>:10: error: not enough arguments for method apply: (v1: Int, v2: Int)Int => (Int => Int) in trait Function2.
Unspecified value parameter v2.
Như bạn có thể thấy, vì danh sách tham số đầu tiên foo
có hai tham số, nên hàm đầu tiên trong chuỗi cà ri có hai tham số.
Tóm lại, các hàm được áp dụng một phần không thực sự khác biệt về hình thức các hàm được xử lý về mặt chức năng. Điều này dễ dàng được xác minh vì bạn có thể chuyển đổi bất kỳ chức năng nào thành chức năng được nấu chín:
scala> (modN _).curried
res45: Int => (Int => Boolean) = <function1
scala> modNCurried _
res46: Int => (Int => Boolean) = <function1>
Đoạn tái bút
Lưu ý: Lý do mà ví dụ của bạn println(filter(nums, modN(2))
hoạt động mà không có dấu gạch dưới sau modN(2)
dường như là trình biên dịch Scala chỉ đơn giản giả định rằng dấu gạch dưới như một sự tiện lợi cho người lập trình.
Bổ sung: Như @asflierl đã chỉ ra một cách chính xác, Scala dường như không thể suy ra loại khi áp dụng một phần các hàm "bình thường":
scala> modN(5, _)
<console>:9: error: missing parameter type for expanded function ((x$1) => modN(5, x$1))
Trong khi thông tin đó có sẵn cho các hàm được viết bằng cách sử dụng ký hiệu danh sách nhiều tham số:
scala> modNCurried(5) _
res3: Int => Boolean = <function1>
Câu trả lời này cho thấy điều này có thể rất hữu ích như thế nào.