2011-01-15 43 views
6

Trong Scala, tôi có thể định nghĩa một hàm với hai danh sách tham số.scala currying bởi các hàm lồng nhau hoặc bởi nhiều danh sách tham số

def myAdd(x :Int)(y :Int) = x + y 

Điều này giúp dễ dàng xác định hàm được áp dụng một phần.

val plusFive = myAdd(5) _ 

Nhưng, tôi có thể thực hiện điều gì đó tương tự bằng cách xác định và trả về hàm lồng nhau.

def myOtherAdd(x :Int) = { 
    def f(y :Int) = x + y 
    f _ 
    } 

Thẩm mỹ, tôi đã di chuyển dấu gạch dưới, nhưng điều này vẫn cảm thấy như cà ri.

val otherPlusFive = myOtherAdd(5) 

Tôi nên sử dụng tiêu chí nào để thích cách tiếp cận này hơn cách khác?

Trả lời

6

Bạn cũng có thể làm điều này:

def yetAnotherAdd(x: Int) = x + (_: Int) 

Bạn nên chọn API dựa trên ý định. Lý do chính trong Scala có nhiều danh sách tham số là giúp suy luận kiểu. Ví dụ:

def f[A](x: A)(f: A => A) = ... 
f(5)(_ + 5) 

Bạn cũng có thể sử dụng nó để có nhiều vararg, nhưng tôi chưa bao giờ thấy mã như vậy. Và, tất nhiên, có nhu cầu về danh sách tham số tiềm ẩn, nhưng đó là một vấn đề khác.

Bây giờ, có nhiều cách bạn có thể có chức năng trả về các chức năng, đó là khá nhiều những gì currying nào. Bạn nên sử dụng chúng nếu API nên được coi là hàm trả về hàm.

Tôi nghĩ khó có thể chính xác hơn điều này.

+0

Cảm ơn, Daniel. Nhận xét của bạn về suy luận kiểu là động lực cho nhiều danh sách tham số là mở mắt. Điều này thực sự hữu ích. –

+0

Động lực khác là viết các DSL tích hợp vào ngôn ngữ độc đáo, giống như 'using (x) {...}'. Mặc dù điều này không, phải thừa nhận, cũng sử dụng các loại truyền hình dành cho nhiều khối. –

9

Có ít nhất bốn cách để thực hiện điều tương tự:

def myAddA(x: Int, y: Int) = x + y 
val plusFiveA: Int => Int = myAddA(5,_) 

def myAddB(x: Int)(y : Int) = x + y 
val plusFiveB = myAddB(5) _ 

def myAddC(x: Int) = (y: Int) => x + y 
val plusFiveC = myAddC(5) 

def myAddD(x: Int) = { 
    def innerD(y: Int) = x + y 
    innerD _ 
} 
val plusFiveD = myAddD(5) 

Bạn có thể muốn biết đó là hiệu quả nhất hoặc đó là phong cách tốt nhất (đối với một số biện pháp không thực hiện dựa tốt nhất).

Theo như hiệu quả, nó chỉ ra rằng tất cả bốn về cơ bản là tương đương. Hai trường hợp đầu tiên thực sự phát ra chính xác cùng một bytecode; JVM không biết gì về nhiều danh sách tham số, vì vậy khi trình biên dịch tính toán nó ra (bạn cần trợ giúp nó với chú thích kiểu trên trường hợp A), tất cả đều giống nhau dưới mui xe. Trường hợp thứ ba cũng rất gần, nhưng vì nó hứa hẹn lên phía trước để trả về một hàm và chỉ định nó ngay tại chỗ, nó có thể tránh được một trường bên trong. Trường hợp thứ tư là khá giống với hai trường hợp đầu tiên về công việc được thực hiện; nó chỉ chuyển đổi sang Function1 bên trong phương thức thay vì bên ngoài.

Về mặt phong cách, tôi đề nghị B và C là những cách tốt nhất để đi, tùy thuộc vào những gì bạn đang làm. Nếu trường hợp sử dụng chính của bạn là tạo một hàm, không phải để gọi tại chỗ với cả hai danh sách tham số, sau đó sử dụng C, vì nó cho bạn biết nó sẽ làm gì. (Ví dụ, phiên bản này cũng đặc biệt quen thuộc với những người đến từ Haskell.) Mặt khác, nếu bạn chủ yếu gọi nó ở vị trí nhưng thỉnh thoảng sẽ cà ri nó, sau đó sử dụng B. Một lần nữa, nó nói rõ ràng hơn nó dự kiến ​​sẽ làm.

+0

Cảm ơn, Rex. Tôi không biết về cách tiếp cận A và C. Câu trả lời của bạn thực sự hữu ích. –

4

Một lợi ích của việc có một phương pháp trả về một hàm trực tiếp (thay vì sử dụng ứng dụng một phần) là nó dẫn đến mã sạch hơn nhiều khi sử dụng ký hiệu ghi vào, cho phép bạn để tránh một bucketload của dấu ngoặc đơn và nhấn mạnh trong các biểu thức phức tạp hơn.

xem xét:

val list = List(1,2,3,4) 

def add1(a: Int)(b: Int) = a + b 
list map { add1(5) _ }  

//versus 

def add2(a: Int) = a + (_: Int) 
list map add2(5) 
Các vấn đề liên quan