Có thể sử dụng một cuộc gọi đến collect
để tạo 2 danh sách mới không? Nếu không, làm thế nào tôi có thể làm điều này bằng cách sử dụng partition
?Phân vùng Scala/Thu thập Sử dụng
Trả lời
collect
(xác định trên TraversableLike và có sẵn trong tất cả các lớp con) làm việc với bộ sưu tập và PartialFunction
. Nó cũng chỉ cần như vậy sẽ xảy ra rằng một loạt các trường hợp điều khoản định nghĩa bên trong niềng răng là một hàm từng phần (Xem phần 8.5 của Scala Language Specification[cảnh báo - PDF])
Như trong xử lý ngoại lệ:
try {
... do something risky ...
} catch {
//The contents of this catch block are a partial function
case e: IOException => ...
case e: OtherException => ...
}
Đó là một cách tiện dụng để xác định một hàm sẽ chỉ chấp nhận một số giá trị của một kiểu nhất định.
Xem xét sử dụng nó trên một danh sách các giá trị hỗn hợp:
val mixedList = List("a", 1, 2, "b", 19, 42.0) //this is a List[Any]
val results = mixedList collect {
case s: String => "String:" + s
case i: Int => "Int:" + i.toString
}
Đối số cho đến collect
phương pháp là một PartialFunction[Any,String]
. PartialFunction
vì không được xác định cho tất cả các mục nhập có thể có của loại Any
(đó là loại List
) và String
vì đó là tất cả các mệnh đề trả về.
Nếu bạn cố gắng sử dụng map
thay vì collect
, giá trị tăng gấp đôi ở cuối mixedList
sẽ gây ra một số MatchError
. Sử dụng collect
chỉ loại bỏ điều này, cũng như bất kỳ giá trị nào khác mà PartialFunction không được xác định.
Một sử dụng có thể sẽ được áp dụng logic khác nhau đến các yếu tố của danh sách:
var strings = List.empty[String]
var ints = List.empty[Int]
mixedList collect {
case s: String => strings :+= s
case i: Int => ints :+= i
}
Mặc dù đây chỉ là một ví dụ, sử dụng các biến có thể thay đổi như thế này là được nhiều người xem là một tội ác chiến tranh - Vì vậy, xin vui lòng đừng làm thế!
Một giải pháp nhiều tốt hơn là sử dụng thu thập hai lần:
val strings = mixedList collect { case s: String => s }
val ints = mixedList collect { case i: Int => i }
Hoặc nếu bạn biết chắc chắn rằng danh sách chỉ chứa hai loại giá trị, bạn có thể sử dụng partition
, mà chia tách một bộ sưu tập vào giá trị tuỳ thuộc vào việc hay không phù hợp với một số vị:
//if the list only contains Strings and Ints:
val (strings, ints) = mixedList partition { case s: String => true; case _ => false }
Việc nắm bắt ở đây là cả hai strings
và ints
là loại List[Any]
, mặc dù bạn có thể dễ dàng ép chúng trở lại một cái gì đó an toàn hơn (có thể bằng cách sử dụng collect
...)
Nếu bạn đã có bộ sưu tập an toàn loại và muốn phân chia một số tài sản khác của các phần tử, thì mọi thứ một chút dễ dàng hơn cho bạn:
val intList = List(2,7,9,1,6,5,8,2,4,6,2,9,8)
val (big,small) = intList partition (_ > 5)
//big and small are both now List[Int]s
Mong rằng tổng hợp hai phương pháp có thể giúp bạn ra khỏi đây!
Không chắc làm thế nào để làm điều đó với collect
mà không cần sử dụng danh sách có thể thay đổi, nhưng partition
có thể sử dụng mô hình kết hợp cũng như (chỉ là một chút dài dòng hơn)
List("a", 1, 2, "b", 19).partition {
case s:String => true
case _ => false
}
@coubeatczech - Bởi vì phân vùng trả về một '(Danh sách [A], Danh sách [A])'. Đó là tất cả những gì nó có thể làm vì đầu vào là một 'List [A]' và một hàm chỉ thị 'A => Boolean'. Nó không có cách nào để biết rằng chức năng chỉ báo có thể là loại cụ thể. –
@Rex Tôi đã định nghĩa phương thức 'collate' của riêng mình để pimp vào các bộ sưu tập giải quyết vấn đề này. Trên một 'List [A]' chữ ký sử dụng là 'collate [B] (fn: PartialFunction [A, B]): (Danh sách (B), List (A))', rõ ràng chữ ký * thực tế * là một chút hairier hơn vì tôi cũng đang sử dụng 'CanBuildFrom' –
Chữ ký của người bình thường được sử dụng collect
trên, nói rằng, Seq
, là
collect[B](pf: PartialFunction[A,B]): Seq[B]
mà thực sự là một trường hợp đặc biệt của
collect[B, That](pf: PartialFunction[A,B])(
implicit bf: CanBuildFrom[Seq[A], B, That]
): That
Vì vậy, nếu bạn sử dụng nó trong chế độ mặc định, câu trả lời là không, chắc chắn không: bạn nhận được chính xác một chuỗi từ nó. Nếu bạn theo dõi CanBuildFrom
qua Builder
, bạn thấy rằng có thể thực hiện That
thực sự là hai chuỗi, nhưng sẽ không có cách nào để được biết chuỗi nào một mục nên đi vào, vì chức năng một phần chỉ có thể nói "có, tôi thuộc về "hoặc" không, tôi không thuộc về ".
Vì vậy, bạn sẽ làm gì nếu bạn muốn có nhiều điều kiện dẫn đến danh sách của bạn được chia thành một loạt các phần khác nhau? Một cách là tạo hàm chỉ báo A => Int
, trong đó A
của bạn được ánh xạ vào một lớp được đánh số, sau đó sử dụng groupBy
. Ví dụ:
def optionClass(a: Any) = a match {
case None => 0
case Some(x) => 1
case _ => 2
}
scala> List(None,3,Some(2),5,None).groupBy(optionClass)
res11: scala.collection.immutable.Map[Int,List[Any]] =
Map((2,List(3, 5)), (1,List(Some(2))), (0,List(None, None)))
Bây giờ bạn có thể tra cứu danh sách con theo lớp (0, 1 và 2 trong trường hợp này). Thật không may, nếu bạn muốn bỏ qua một số yếu tố đầu vào, bạn vẫn phải đặt chúng vào một lớp học (ví dụ: bạn có thể không quan tâm đến nhiều bản sao của None
trong trường hợp này).
Tôi sử dụng tính năng này. Một điều tốt đẹp về nó là nó kết hợp phân vùng và ánh xạ trong một lần lặp. Một nhược điểm là nó phân bổ một loạt các đối tượng tạm thời (các Either.Left
và Either.Right
trường hợp)
/**
* Splits the input list into a list of B's and a list of C's, depending on which type of value the mapper function returns.
*/
def mapSplit[A,B,C](in: List[A])(mapper: (A) => Either[B,C]): (List[B], List[C]) = {
@tailrec
def mapSplit0(in: List[A], bs: List[B], cs: List[C]): (List[B], List[C]) = {
in match {
case a :: as =>
mapper(a) match {
case Left(b) => mapSplit0(as, b :: bs, cs )
case Right(c) => mapSplit0(as, bs, c :: cs)
}
case Nil =>
(bs.reverse, cs.reverse)
}
}
mapSplit0(in, Nil, Nil)
}
val got = mapSplit(List(1,2,3,4,5)) {
case x if x % 2 == 0 => Left(x)
case y => Right(y.toString * y)
}
assertEquals((List(2,4),List("1","333","55555")), got)
tôi không thể tìm thấy một giải pháp đáp ứng cho vấn đề cơ bản ở đây. Tôi không cần một bài giảng trên collect
và không quan tâm nếu đây là bài tập về nhà của ai đó. Ngoài ra, tôi không muốn một cái gì đó mà chỉ hoạt động cho List
.
Vì vậy, đây là cú đâm của tôi vào đó. Hiệu quả và tương thích với bất kỳ TraversableOnce
, thậm chí dây:
implicit class TraversableOnceHelper[A,Repr](private val repr: Repr)(implicit isTrav: Repr => TraversableOnce[A]) {
def collectPartition[B,Left](pf: PartialFunction[A, B])
(implicit bfLeft: CanBuildFrom[Repr, B, Left], bfRight: CanBuildFrom[Repr, A, Repr]): (Left, Repr) = {
val left = bfLeft(repr)
val right = bfRight(repr)
val it = repr.toIterator
while (it.hasNext) {
val next = it.next
if (!pf.runWith(left += _)(next)) right += next
}
left.result -> right.result
}
def mapSplit[B,C,Left,Right](f: A => Either[B,C])
(implicit bfLeft: CanBuildFrom[Repr, B, Left], bfRight: CanBuildFrom[Repr, C, Right]): (Left, Right) = {
val left = bfLeft(repr)
val right = bfRight(repr)
val it = repr.toIterator
while (it.hasNext) {
f(it.next) match {
case Left(next) => left += next
case Right(next) => right += next
}
}
left.result -> right.result
}
}
Ví dụ cách dùng:
val (syms, ints) =
Seq(Left('ok), Right(42), Right(666), Left('ko), Right(-1)) mapSplit identity
val ctx = Map('a -> 1, 'b -> 2) map {case(n,v) => n->(n,v)}
val (bound, unbound) = Vector('a, 'a, 'c, 'b) collectPartition ctx
println(bound: Vector[(Symbol, Int)], unbound: Vector[Symbol])
- 1. Sử dụng PHP để tạo ngẫu nhiên số thập phân Beteween Hai số thập phân
- 2. cách phân tích cú pháp thập phân thập phân hoặc thập phân trong Python
- 3. Sử dụng phân vùng trao đổi trong R
- 4. mảng phân vùng sử dụng chỉ mục trong ruby
- 5. chop số thập phân chưa sử dụng với javascript
- 6. Tại sao sử dụng hằng số thập lục phân?
- 7. Dấu thập phân hoặc dấu phẩy thập phân trong Android
- 8. lấy phần thập phân của số thập phân
- 9. Chuyển đổi thập phân sang thập phân trong lisp chung
- 10. Khi nào sử dụng Phân vùng không gian nhị phân, Quadtree, Octree?
- 11. Java thập lục phân
- 12. Tạo các biểu mẫu Django sử dụng dấu phẩy làm dấu phân cách thập phân
- 13. phân vùng và UPDATE
- 14. MySQL 5.1 Phân vùng
- 15. C# sizeof thập phân?
- 16. Phân vùng bảng là gì?
- 17. Phân vùng MYSQL là gì?
- 18. Members javascript số thập phân
- 19. Phân vùng tích phân Python với phân vùng k đã cho
- 20. Trong C# có số thập phân và số thập phân khác nhau?
- 21. Phân vùng Bảng hiện có
- 22. Phân vùng FAT32 nhỏ nhất
- 23. Mysql, handlersocket và phân vùng?
- 24. Số ứng dụng web trên iPhone với số thập phân
- 25. Lợi ích của phân vùng dọc VS phân vùng ngang là gì?
- 26. Căn chỉnh dữ liệu thập phân trong cột bảng theo dấu thập phân, HTML5, CSS3
- 27. Loại thập phân trong php
- 28. 0x00000000 hệ thập lục phân?
- 29. Python Hệ thập lục phân
- 30. chuyển đổi dấu thập phân
Giải thích rất hay, nhưng những gì tôi nghĩ OP muốn là sự kết hợp của' collection' và 'partition' trả về một danh sách các giá trị được thu thập và một danh sách của tất cả các phần còn lại. 'def collectAndPartition [A, B] (pf: PartialFunction [A, B]): (Danh sách [B], Danh sách [A])'. Điều này có lẽ sẽ đạt được một cách tao nhã nhất với một hàm thư viện riêng, tức là trong nguồn 'collect' trong TraversableLike chúng ta có' for (x <- this) nếu (pf.isDefinedAt (x)) b + = pf (x) ' , người ta có thể chỉ đơn giản là tack một 'else a + = x' vào cuối của đó, nơi' a' sẽ là một người xây dựng cho danh sách của tất cả các phần còn lại. –
Tôi biết OP cần gì, và tôi cũng biết rằng đây là một câu hỏi về bài tập về nhà (gần đây đã được đề cập trên chồng tràn), vì vậy tôi sẵn sàng cung cấp nhiều lý thuyết mà không thực sự giải quyết nó. Đối với collectAndPartition, tôi đã viết rằng, mặc dù tôi đã đặt tên cho phương thức 'collate'.Nếu bất cứ ai đang dạy scala đến mức mà sinh viên được dự kiến sẽ làm việc với CanBuildFrom thì tôi sẽ rất ngạc nhiên, nó vượt ra ngoài hầu hết mọi người hiện đang sử dụng scala trong sản xuất. –
Điều đó rất hữu ích. Nhưng tôi vẫn đang nghĩ ... liệu có thể tách biệt các giá trị tích cực và tiêu cực, mà không tạo ra "tội phạm chiến tranh" khi bạn viết sai lầm hơn? Tôi chỉ can đảm vì tôi đã làm bài tập về nhà với việc sử dụng phân vùng. Ohhh ... Và nhân tiện, Cảm ơn bạn đã giúp đỡ! –