2011-12-16 28 views
7

Tôi đến từ Groovy và nó có phương thức .with trên mọi loại chấp nhận đóng một đối số; đối số là đối tượng mà phương thức .with đang được gọi. Điều này cho phép một kỹ thuật rất tuyệt vời để mở rộng các chức năng chuỗi chức năng, giúp bạn thoát khỏi nghĩa vụ giới thiệu các biến tạm thời, các yếu tố mã của bạn, giúp dễ đọc và thực hiện các tính năng khác..với thay thế trong scala

Tôi muốn để có thể làm điều gì đó như thế này:

Seq(1, 2, 3, 4, 5) 
    .filter(_ % 2 == 0) 
    .with(it => if (!it.isEmpty) println(it)) 

Thay vì

val yetAnotherMeaninglessNameForTemporaryVariable = 
    Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0) 
if (!yetAnotherMeaninglessNameForTemporaryVariable.isEmpty) 
    println(yetAnotherMeaninglessNameForTemporaryVariable) 

Nói cách khác trong ví dụ đầu tiên .with là kinda giống với .foreach nhưng thay vì lặp qua các mục của đối tượng mà nó đang được gọi một lần trên chính đối tượng đó. Vì vậy, it bằng Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0).

Kể từ khi tôi đã rất ngạc nhiên không tìm thấy bất cứ điều gì như thế trong Scala, câu hỏi của tôi là:

  • tôi thiếu cái gì?
  • có bất kỳ kỹ thuật thay thế nào có nguồn gốc từ Scala không?
  • nếu không, có lý do chính đáng nào khiến tính năng này không được triển khai trong Scala không?

Cập nhật: Một yêu cầu tính năng thích hợp đã được đăng tải trên theo dõi vấn đề Scala: https://issues.scala-lang.org/browse/SI-5324. Vui lòng bỏ phiếu và quảng cáo

+1

Chỉ cần lưu ý: 'with' là một từ dành riêng trong Scala, vì vậy phương pháp này không thể được đặt tên giống nhau. Nó vẫn còn tồn tại dưới một cái tên khác; đây là _câu hỏi và câu trả lời phổ biến nhất của Scala trên StackOverflow theo như tôi có thể nói ("nó không tồn tại; làm cho riêng bạn như thế này")! –

+0

Tôi nghĩ cái tên 'convert' sẽ phù hợp nhất, do đó gợi ý rằng phương pháp này sẽ không có tác dụng phụ và vì nó lấy người gọi làm tham số và trả về một cái gì đó mới, nó phải là một loại chuyển đổi. Trong ý nghĩa đó chức năng này sẽ không thể thay thế trong thư viện chuẩn. Cũng như được đề xuất trong http://stackoverflow.com/a/8538277/485115 cũng có một biến thể tác dụng phụ có tên là 'tap', trả về đối tượng người gọi. –

Trả lời

12

Không tồn tại bất kỳ phương pháp nào như vậy trong thư viện chuẩn, nhưng không khó để xác định của riêng bạn.

implicit def aW[A](a: A) = new AW(a) 
class AW[A](a: A) { 
    def tap[U](f: A => U): A = { 
    f(a) 
    a 
    } 
} 

val seq = Seq(2, 3, 11). 
      map(_ * 3).tap(x => println("After mapping: " + x)). 
      filter(_ % 2 != 0).tap(x => println("After filtering: " + x)) 

EDIT: (để đáp ứng với những nhận xét)

Ồ, tôi hiểu lầm. Những gì bạn cần là có trong thư viện Scalaz. Nó có tên |> (được gọi là nhà khai thác đường ống). Cùng với đó, ví dụ của bạn sẽ trông giống như hình dưới đây:

Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0) |> { it => if(!it.isEmpty) println(it) } 

Nếu bạn không thể sử dụng Scalaz, bạn có thể xác định các nhà điều hành của mình:

implicit def aW[A](a: A) = new AW(a) 
class AW[A](a: A) { 
    def |>[B](f: A => B): B = f(a) 
} 

Và nó không phải là một thực tế xấu để dắt phương pháp hữu ích (s) trên các loại hiện có.Bạn nên sử dụng các chuyển đổi ngầm một cách tiết kiệm, nhưng tôi nghĩ rằng hai bộ kết hợp này là đủ phổ biến để các pimps của chúng được chính đáng.

+0

Có Tôi biết rằng tôi có thể mở rộng thư viện chuẩn với hàm ý, nhưng tôi chỉ tìm thấy bước ra khỏi tiêu chuẩn một thực tế xấu. Đó là lý do tại sao câu hỏi của tôi là tại sao các chàng trai Scala không thực hiện điều này, bởi vì có lẽ điều này nên được đăng trên bộ theo dõi vấn đề của họ. Bên cạnh hàm 'tap' của bạn mặc dù rất hữu ích có một chút khác biệt so với' with' theo nghĩa là thay vì kết quả của việc đóng, nó trả về chính đối tượng đó. Sẽ rất tuyệt khi có cả '.with' và' .tap' trong thư viện chuẩn IMO. –

+0

Tôi đặt tên cho cùng một phương thức 'hiệu ứng'. Tại sao 'tap'? Tôi thích nó ngắn hơn, nhưng tôi thiếu lợi ích nhớ. –

+3

@NikitaVolkov Odersky chỉ trích thói quen của một số lập trình viên Scala tránh việc khai báo 'val' trung gian bằng mọi giá, mà' |> 'chắc chắn góp phần, khi người điều hành đường ống được yêu cầu. Vì vậy, bây giờ, tôi không tin rằng nó sẽ trở thành một phần của thư viện chuẩn. –

1

Nhớ cuộc gọi theo tên? Có lẽ nó mang lại cho bạn những capablity bạn muốn:

object Test { 
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) 
    t 
} 
} 

như mô tả trong http://www.tutorialspoint.com/scala/functions_call_by_name.htm

3

Hãy thử sth như thế này.

println(Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0).ensuring(!_.isEmpty)) 

Ném ngoại lệ xác nhận nếu điều kiện không được đáp ứng.

+0

+1, mặc dù nó sẽ chỉ hoạt động đối với các lần thực hiện câu lệnh đơn (ví dụ: println như được trích dẫn ở đây). nếu bạn muốn thực hiện nhiều thao tác, nó sẽ trở nên phức tạp. – aishwarya

+0

Kỹ thuật tốt cho các mục đích khác. Đối với điều này ném một ngoại lệ là overkill –

7

Có một số cú pháp cho mô hình này bao gồm trong Scala:

Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0) match { case it => if (!it.isEmpty) println(it) } 

Tuy nhiên, đây không phải là thành ngữ được chấp nhận, do đó bạn có lẽ nên tránh (ab) sử dụng nó.

Nếu bạn không thích phát minh tải và tải các tên cho biến giả, hãy nhớ rằng bạn có thể sử dụng phạm vi niềng răng:

val importantResult = { 
    val it = Seq(1,2,3).filter(_ % 2 == 0) 
    if (!it.isEmpty) println(it) 
    it 
} 

val otherImportantResultWithASpeakingVariableName = { 
    val it = // ... 
    /* ... */ 
    it 
} 
+0

Đề xuất tốt đẹp về trận đấu! Mặc dù một chút thô, nó trả lời đầu tiên 2 câu hỏi của tôi. –

1

Mặc dù tôi thích giải pháp khác tốt hơn (vì họ là địa phương hơn và do đó dễ dàng hơn để làm theo) , đừng quên rằng bạn có thể

{ val x = Seq(1,2,3,4,5).filter(_ % 2 == 0); println(x); x } 

để tránh va chạm tên trên biến vô nghĩa của bạn và giữ chúng bị hạn chế trong phạm vi thích hợp.

1

Đây chỉ là hoạt động ứng dụng f(x) lộn trên đầu của nó: x.with(f) ... Nếu bạn đang tìm kiếm một cách thành ngữ làm with tại Scala, un-lật nó:

(it => if (!it.isEmpty) println(it)) (Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0)) 

Tương tự, nếu bạn muốn x.with(f).with(g), chỉ cần sử dụng g(f(x)) ...

+2

': 8: lỗi: thiếu loại tham số' – david

Các vấn đề liên quan