2012-08-04 34 views
5

Trong chức năng tham số hóa này, tại sao tôi cần dàn diễn viên? Và làm thế nào tôi có thể loại bỏ nó?Trong hàm Scala được tham số hóa này, tại sao tôi cần dàn diễn viên?

/** Filters `xs` to have only every nth element. 
    */ 
def everyNth[A <% Iterable[B], B](xs: A, n: Int, offset: Int = 0): A = 
    (xs.zipWithIndex collect { case (x, i) if (i - offset) % n == 0 => x }).asInstanceOf[A] 

Nếu tôi không có dàn diễn viên cuối cùng, tôi nhận được thông báo lỗi này:

type mismatch; found : Iterable[B] required: A 

Chức năng này (với các diễn viên) làm việc cho tất cả các trường hợp tôi đã thử nó trên và tôi biết từ việc nhập những thứ như sau tại REPL rằng Scala có thể suy ra loại kết quả chính xác khi không ở trong ngữ cảnh của hàm được tham số:

scala> val a: Stream[Int] = (Stream.from(0).zipWithIndex collect { case (x, i) if (i + 3) % 5 == 0 => x }) 
a: Stream[Int] = Stream(2, ?) 

scala> a take 10 force 
res20: scala.collection.immutable.Stream[Int] = Stream(2, 7, 12, 17, 22, 27, 32, 37, 42, 47) 

Vui lòng giải thích!

+0

Câu hỏi tương tự sử dụng 'CanBuildFrom' để khắc phục sự cố: [Hàm tổng quát có kiểu và trả về cùng loại] (http://stackoverflow.com/questions/10019529/function-which-generically-takes- a-type-and-returns-the-same-type). Tôi không thể làm cho nó để làm việc với câu hỏi này, một người nào khác? – sschaef

+0

Tôi nhận CanBuildFrom để làm việc cho câu hỏi của mình và đưa giải pháp vào câu trả lời. Xem câu trả lời dưới đây nếu bạn tò mò. – Douglas

+0

Câu trả lời hay! Btw, bạn có thể chấp nhận câu trả lời của riêng bạn ... – sschaef

Trả lời

4

Theo một số một số gợi ý trong ý kiến, tôi nhìn vào CanBuildFrom, và đây là những gì tôi đã đưa ra:

import scala.collection.IterableLike 
import scala.collection.generic.CanBuildFrom 

/** Filters `xs` to have only every nth element. 
    */ 
def everyNth[A, It <: Iterable[A]] 
     (xs: It with IterableLike[A, It], n: Int, offset: Int = 0) 
     (implicit bf: CanBuildFrom[It, A , It]): It = { 
    val retval = bf() 
    retval ++= xs.zipWithIndex collect { case (x, i) if (i - offset) % n == 0 => x } 
    retval.result  
} 

Yay, nó hoạt động !!!

Và có NO truyền. Như vậy, nó thậm chí làm việc cho Ranges.

Tuy nhiên, phải bắt đầu với một khoảng trống và sau đó sử dụng "++ =" để điền vào nó có vẻ hơi không phù hợp, vì vậy nếu có ai có một giải pháp thanh lịch hơn, tôi là tất cả tai.

Đây là một hàm tổng quát khác mà tôi đã triển khai thực hiện phức tạp hơn một chút so với ở trên vì kiểu trả về không giống như kiểu đối số. I E., Đầu vào là một chuỗi các A 's, nhưng sản lượng là một chuỗi các (A, A)' s:

def zipWithSelf[A, It[A] <: Iterable[A]] 
     (xs: It[A] with IterableLike[A, It[A]]) 
     (implicit bf: CanBuildFrom[It[A], (A, A), It[(A, A)]]): It[(A, A)] = { 
    val retval = bf() 
    if (xs.nonEmpty) { 
     retval ++= xs zip xs.tail 
     retval.result 
    } else retval.result 
} 

Và đây là một:

/** Calls `f(x)` for all x in `xs` and returns an Iterable containing the indexes for 
    * which `f(x)` is true. 
    * 
    * The type of the returned Iterable will match the type of `xs`. 
    */ 
def findAll[A, It[A] <: Iterable[A]] 
     (xs: It[A] with IterableLike[A, It[A]]) 
     (f: A => Boolean) 
     (implicit bf: CanBuildFrom[It[A], Int, It[Int]]): It[Int] = { 
    val retval = bf() 
    retval ++= xs.zipWithIndex filter { p => f(p._1) } map { _._2 } 
    retval.result 
} 

tôi vẫn không có bất cứ sự hiểu biết sâu sắc về các kiểu "Thích" và CanBuildFrom, nhưng tôi có được ý chính. Và rất dễ dàng trong hầu hết các trường hợp để viết phiên bản đúc của một hàm chung như là một lần đầu tiên, và sau đó thêm CanBuildFromIterableLike boilerplate để làm cho chức năng tổng quát hơn và an toàn kiểu.

3

Có một số trường hợp collect không trả lại subtype cùng Iterable như nó đã được kêu gọi, ví dụ trong trường hợp của một Range:

scala> everyNth(1 to 10, 2) 
java.lang.ClassCastException: scala.collection.immutable.Vector cannot be cast to scala.collection.immutable.Range$Inclusive 
     at .<init>(<console>:9) 
     at .<clinit>(<console>) 
     at .<init>(<console>:11) 
     at .<clinit>(<console>) 
     at $print(<console>) 
     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
     at java.lang.reflect.Method.invoke(Method.java:616) 
     at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:704) 
     at scala.tools.nsc.interpreter.IMain$Request$$anonfun$14.apply(IMain.scala:920) 
     at scala.tools.nsc.interpreter.Line$$anonfun$1.apply$mcV$sp(Line.scala:43) 
     at scala.tools.nsc.io.package$$anon$2.run(package.scala:25) 
     at java.lang.Thread.run(Thread.java:679) 
+0

Ah, tất nhiên rồi. Phạm vi ngu ngốc! Có một số đặc điểm khác có thể được sử dụng không bao gồm các chuỗi hành vi không được xử lý như vậy không? Hay tôi nên sống với dàn diễn viên? – Douglas

+0

Tôi đoán cách thích hợp để làm điều đó là sử dụng phép thuật CanBuildFrom được sử dụng trong API thu thập? –

+0

Tôi có CanBuildFrom để làm việc cho câu hỏi của mình và đưa giải pháp vào câu trả lời cùng với câu hỏi này. – Douglas

1

Vấn đề ở đây là, rằng bằng cách gọi collect trên xs bạn chuyển đổi nó thành Iterable[B]. A <% Iterable[B] có nghĩa là, A có thể được xem là Iterable[B], điều này không nhất thiết có nghĩa là Iterable[B] cũng có thể được xem là A. Điều gì thực sự xảy ra ở đây là

def everyNth[A, B](xs: A, n: Int, offset: Int = 0)(implicit view: (A => Iterable[B])): A = 
    (view(xs).zipWithIndex collect { 
    case (x, i) if (i + offset) % n == 0 => x 
    }).asInstanceOf[A] 

Khi tôi có ví dụ này:

class Foo 
implicit def foo2Iterable(foo: Foo) = List(foo) 

và gọi

everyNth(new Foo, 2) 

tôi nhận được

java.lang.ClassCastException: scala.collection.immutable.$colon$colon cannot be cast to Foo 

Bạn nên tránh đúc ở đây. Bạn thêm chế độ xem từ số Iterable[B] => A

chỉnh sửa: loại giới hạn không hoạt động ở đây.

+0

Việc thay thế khung nhìn bị ràng buộc với một kiểu ràng buộc không loại bỏ được sự cần thiết của một diễn viên ở đây, vì vậy tôi không rõ ràng chính xác những gì bạn đang khẳng định. – Douglas

+0

Xin lỗi, bạn đã đúng. Thêm một khung nhìn từ Iterable [B] => A sẽ là giải pháp duy nhất ở đây. – drexin

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