Jak podzielić sekwencję na dwie listy według predykatu?
Alternatywa: mogę użyć filter
i filterNot
lub napisać własną metodę, ale czy nie ma lepszej, bardziej ogólnej (wbudowanej) metody?
Jak podzielić sekwencję na dwie listy według predykatu?
Alternatywa: mogę użyć filter
i filterNot
lub napisać własną metodę, ale czy nie ma lepszej, bardziej ogólnej (wbudowanej) metody?
Odpowiedzi:
Przy użyciu partition
metody:
scala> List(1,2,3,4).partition(x => x % 2 == 0)
res0: (List[Int], List[Int]) = (List(2, 4),List(1, 3))
_ % 2 == 0
.
Dobrze, że partition
było rzeczą, którą chciał - nie ma innej metody, które również wykorzystuje predykatu podzielić listę na dwie części: span
.
Pierwsza z nich, partycja , umieści wszystkie „prawdziwe” elementy na jednej liście, a pozostałe na drugiej liście.
span umieści wszystkie elementy na jednej liście, dopóki element nie będzie „fałszywy” (w sensie predykatu). Od tego momentu elementy zostaną umieszczone na drugiej liście.
scala> Seq(1,2,3,4).span(x => x % 2 == 0)
res0: (Seq[Int], Seq[Int]) = (List(),List(1, 2, 3, 4))
Możesz rzucić okiem na scalex.org - umożliwia ona przeszukiwanie standardowej biblioteki scala w poszukiwaniu funkcji według ich podpisu. Na przykład wpisz:
List[A] => (A => Boolean) => (List[A], List[A])
Zobaczysz partycję .
Możesz także użyć foldLeft, jeśli potrzebujesz czegoś dodatkowego. Właśnie napisałem taki kod, gdy partycja go nie przecięła:
val list:List[Person] = /* get your list */
val (students,teachers) =
list.foldLeft(List.empty[Student],List.empty[Teacher]) {
case ((acc1, acc2), p) => p match {
case s:Student => (s :: acc1, acc2)
case t:Teacher => (acc1, t :: acc2)
}
}
Wiem, że mogę się spóźnić na przyjęcie i są bardziej szczegółowe odpowiedzi, ale możesz zrobić dobry użytek z tego groupBy
val ret = List(1,2,3,4).groupBy(x => x % 2 == 0)
ret: scala.collection.immutable.Map[Boolean,List[Int]] = Map(false -> List(1, 3), true -> List(2, 4))
ret(true)
res3: List[Int] = List(2, 4)
ret(false)
res4: List[Int] = List(1, 3)
To sprawia, że twój kod jest nieco bardziej przyszłościowy, jeśli chcesz zmienić warunek na coś innego niż logiczne.
Jeśli chcesz podzielić listę na więcej niż 2 części i zignorować granice, możesz użyć czegoś takiego (zmodyfikuj, jeśli chcesz szukać int)
def split(list_in: List[String], search: String): List[List[String]] = {
def split_helper(accum: List[List[String]], list_in2: List[String], search: String): List[List[String]] = {
val (h1, h2) = list_in2.span({x: String => x!= search})
val new_accum = accum :+ h1
if (h2.contains(search)) {
return split_helper(new_accum, h2.drop(1), search)
}
else {
return accum
}
}
return split_helper(List(), list_in, search)
}
// TEST
// split(List("a", "b", "c", "d", "c", "a"), {x: String => x != "x"})
val (even, odd) = List(1,2,3,4).partition(x => x % 2 == 0)
jest sposobem na zniszczenie powstałej krotkipartition
w czytelny sposób.