2011-09-02 4 views
9

Tôi đang cố gắng hiểu điểm của tính năng ngôn ngữ này của nhiều mệnh đề tham số và lý do bạn sẽ sử dụng nó. Ví dụ, sự khác biệt giữa hai hàm này thực sự là gì?Điểm của nhiều mệnh đề tham số trong định nghĩa hàm trong Scala là gì?

class WTF { 
    def TwoParamClauses(x : Int)(y: Int) = x + y 
    def OneParamClause(x: Int, y : Int) = x + y 
} 

>> val underTest = new WTF 
>> underTest.TwoParamClauses(1)(1) // result is '2' 
>> underTest.OneParamClause(1,1) // result is '2' 

Có gì đó trong số này Scala specification at point 4.6. Xem nếu điều đó có ý nghĩa với bạn không.

NB: spec gọi các mệnh đề 'tham số' này, nhưng tôi nghĩ một số người cũng có thể gọi chúng là 'danh sách tham số'.

+0

nCác-scala lập trình viên ở đây. Có lẽ nó chỉ là một phong cách khác nhau? Không phải mọi thứ đều chỉ có một cách để thực hiện nó. – apscience

+0

được rồi, có một câu trả lời cho câu trả lời này của Daniel Sobral cho http://stackoverflow.com/questions/4697404/scala-currying-by-nested-functions-or-by-multiple-parameter-lists –

+0

được chấp nhận câu trả lời ở đây có thể trả lời câu hỏi này: (1) vì vậy bạn không phải chỉ rõ loại từ mệnh đề param đầu tiên trong mệnh đề thứ hai; (2) cho sự linh hoạt của thiết kế thư viện; (3) để làm cho cà ri dễ dàng hơn: http://stackoverflow.com/questions/4915027/two-ways-of-currying-in-scala-whats-the-use-case-for-each –

Trả lời

9

Dưới đây là ba công dụng thực tế của nhiều danh sách tham số,

  1. Để hỗ trợ kiểu suy luận. Điều này đặc biệt hữu ích khi sử dụng các phương thức đặt hàng cao hơn. Dưới đây, các tham số kiểu A của g2 được suy ra từ các tham số đầu tiên x, vì vậy các đối số chức năng trong tham số thứ hai có thể được f elided,

    def g1[A](x: A, f: A => A) = f(x) 
    g1(2, x => x) // error: missing parameter type for argument x 
    
    def g2[A](x: A)(f: A => A) = f(x) 
    g2(2) {x => x} // type is inferred; also, a nice syntax 
    
  2. Đối với các thông số ngầm. Chỉ danh sách tham số cuối cùng mới có thể được đánh dấu là ẩn, và một danh sách tham số duy nhất không thể kết hợp các tham số ngầm định và không ngầm. Định nghĩa của g3 dưới đây đòi hỏi phải có hai danh sách tham số,

    // analogous to a context bound: g3[A : Ordering](x: A) 
    def g3[A](x: A)(implicit ev: Ordering[A]) {} 
    
  3. Để thiết lập các giá trị mặc định dựa trên các thông số trước,

    def g4(x: Int, y: Int = 2*x) {} // error: not found value x 
    def g5(x: Int)(y: Int = 2*x) {} // OK 
    
3

Có một số trường hợp phân biệt này quan trọng:

  1. Nhiều danh sách tham số cho phép bạn có những thứ như TwoParamClauses (2); đó là chức năng được tạo tự động của kiểu Int => Int, nó thêm 2 vào đối số của nó. Tất nhiên bạn có thể tự định nghĩa chính mình bằng cách sử dụng OneParamClause, nhưng nó sẽ mất nhiều lần nhấn phím hơn

  2. Nếu bạn có hàm với tham số ngầm định cũng có tham số rõ ràng, tham số ngầm phải là tất cả trong mệnh đề tham số riêng của chúng. điều này có vẻ như là một hạn chế tùy ý nhưng thực sự khá hợp lý)

Ngoài ra, tôi nghĩ, sự khác biệt là phong cách.

8

TwoParamClause liên quan đến hai lần gọi phương thức trong khi OneParamClause chỉ gọi phương thức chỉ một lần. Tôi nghĩ cụm từ bạn đang tìm kiếm là currying. Trong số nhiều trường hợp sử dụng, nó giúp bạn phân tích tính toán thành các bước nhỏ. Điều này answer có thể thuyết phục bạn về tính hữu ích của currying.

+1

Biểu mẫu với hai mệnh đề tham số chỉ gọi phương thức * * một lần - kết quả của lời gọi đó là một hàm * mà sau đó được áp dụng dẫn đến giá trị cuối cùng. –

+1

@pst: Nhưng việc áp dụng một hàm cũng gọi một phương thức (cụ thể là phương thức 'apply' của hàm). –

+0

@Alexey Romanov True. Trong bài bình luận này, tôi đã cố gắng nhấn mạnh sự khác biệt giữa một phương thức (một thứ mà một đối tượng biết cách "trả lời") và một hàm (một đối tượng rời rạc có thể được áp dụng, thậm chí là nếu ứng dụng như vậy là kết quả của việc gọi một phương thức trên nó). Tôi cũng vẫn không đồng ý với thuật ngữ "...chỉ gọi hàm một lần "vì nó gọi phương thức' OneParamClause' * * chỉ một lần. –

4

Nhiều danh sách tham số có thể giúp loại scala suy luận để biết thêm chi tiết xem: Making the most of Scala's (extremely limited) type inference

Loại thông tin không chảy từ trái sang phải trong một danh sách đối số, chỉ từ trái sang phải trên danh sách đối số. Vì vậy, mặc dù Scala biết các loại của hai đối số đầu tiên ... thông tin đó không chảy vào chức năng ẩn danh của chúng ta.

...

Bây giờ chức năng nhị phân của chúng tôi là trong một danh sách đối số riêng biệt, bất kỳ loại thông tin từ danh sách đối số trước đó được sử dụng để điền vào các loại cho chức năng của chúng tôi ... do đó chúng tôi không cần chú thích thông số lambda của chúng tôi.

+0

Hãy đảm bảo bao gồm các đoạn trích và/hoặc lý luận chứ không phải chỉ các liên kết trống trong các bài đăng. –

+0

Tôi nghĩ rằng mô tả ngắn gọn của tôi sẽ đủ để trêu chọc và để hoàn thành một tài liệu tham khảo nên đọc blog. Nhưng cách này cũng tốt :) – AndreasScheinert

6

Có sự khác biệt giữa cả hai phiên bản liên quan đến suy luận kiểu.Cân nhắc

def f[A](a:A, aa:A) = null 
f("x",1) 
//Null = null 

Ở đây, loại A là ràng buộc để Any, mà là một loại siêu StringInt. Nhưng:

def g[A](a:A)(aa:A) = null 
g("x")(1) 

error: type mismatch; 
found : Int(1) 
required: java.lang.String 
     g("x")(1) 
      ^

Như bạn thấy, kiểm tra loại chỉ xem xét danh sách đối số đầu tiên, vì vậy A bị ràng buộc để String, vì vậy giá trị Int cho aa trong danh sách đối số thứ hai là một lỗi type.