Jak posortować tablicę w Scali?

81

Widzę, że znajduje się na nim obiekt sortujący Sortingz metodą szybkiego sortowaniaquickSort .

Jaki byłby przykład kodu użycia go, sortowania tablicy obiektów dowolnego typu? Wygląda na to, że muszę przekazać implementację Orderablecechy, ale nie jestem pewien składni.

Wolałabym również odpowiedzi robiąc to w „sposób Scali”. Wiem, że mogę po prostu użyć biblioteki Java.

Dan Gravell
źródło

Odpowiedzi:

32

Sorting.quickSort deklaruje funkcje do pobierania tablicy liczb lub łańcuchów, ale zakładam, że chcesz posortować listę obiektów własnych klas?

Myślę, że funkcja, na którą patrzysz, to

quickSort [K](a : Array[K])(implicit view$1 : (K) => Ordered[K]) : Unit

Co, jeśli dobrze to czytam, oznacza, że ​​obiekty w tablicy muszą mieć tę Orderedcechę. Twoja klasa musi więc rozszerzać Ordered(lub musi ją mieszać), a zatem musi implementować comparemetodę tej cechy.

A więc, aby wyrwać przykład z książki:

class MyClass(n: Int) extends Ordered[MyClass] {
   ...
  def compare(that: MyClass) =
    this.n - that.n
}

Zatem mając Array [MyClass], Sorting.quickSort powinno działać.

skaffman
źródło
Tak, to ten. Teraz to ma sens. Jak bym to „wmieszał”?
Dan Gravell
Zobacz edycję. Możesz albo rozszerzyć jak w moim przykładzie, albo użyć „class MyClass extends OtherClass with Ordered [MyClass]”, co jest mieszanką.
skaffman
Dzięki. Myślę, że zrobię to drugie.
Dan Gravell
6
Nie żeby być pedantycznym, ale ... twoja klasa nie musi rozszerzać Ordered, wystarczy, że istnieje widok (znany również jako niejawna konwersja) w zakresie z twojej klasy do czegoś, co rozszerza Ordered.
Kim Stebel
100

Dzięki Scali 2.8 lub nowszej można:

List(3,7,5,2).sortWith(_ < _)

który używa java.util.Arrays.sort , implementacji quicksort.

adelarsq
źródło
2
Korzystając z niejawnego uporządkowania, jeszcze krótszego: List (3,7,5,2) .sorted as per scala.collection.immutable.LinearSeq
Utgarda
Skąd wiemy, że sortWith używa java.util.Arrays.sort? Czy jest do tego odniesienie?
deepkimo
To jest trochę inne niż to, o co pytano w pierwotnym pytaniu. Z pewnością działa, aby utworzyć nową posortowaną listę (lub tablicę, jak podano w oryginalnym pytaniu), ale sortWith zwraca nowy obiekt w przeciwieństwie do sortowania oryginalnej listy lub tablicy w miejscu.
rphutchinson
57

Obecnie ten też działa:

List(3,7,5,2).sorted

hendrik
źródło
I przypuszczam, że sortowanie w odwrotnej kolejności jest idiomatyczne List(3,7,5,2).sorted.reverse?
Nick Chammas
19

Jeśli chcesz po prostu posortować rzeczy, ale nie masz związku z obiektem Sorting w szczególności, możesz użyć metody sortowania List. Pobiera funkcję porównawczą jako argument, więc możesz jej używać na dowolnych typach:

List("Steve", "Tom", "John", "Bob").sort((e1, e2) => (e1 compareTo e2) < 0)

List(1, 4, 3, 2).sort((e1, e2) => (e1 < e2))

Listy prawdopodobnie kwalifikują się jako „bardziej skromne” niż tablice.

Z scala api docs :

def sort (lt: (A, A) => Boolean): Lista [A]

Sort the list according to the comparison function <(e1: a, e2: a) =>

Boolean, który powinien być prawdziwy, jeśli e1 jest mniejsze niż e2.

Peter Recore
źródło
Hmm, więc List ma metodę sortowania, ale Array nie. Jak niezadowalająco nieregularne. Oczekuję tego rodzaju bzdur po Java API, ale nie w Scali.
skaffman
Jestem zbyt wielkim nowicjuszem, żeby wiedzieć na pewno, ale może to być zło konieczne, biorąc pod uwagę, że Scala zachowuje zgodność ze wszystkimi rzeczami Java. Z drugiej strony Scala robi tyle magii, że wydaje się normalne, że chce, by robiła jeszcze więcej!
Peter Recore
5
List (...) sort {_ <_} byłoby bardziej idiomatyczne.
Daniel Spiewak
2
Warto również zauważyć, że String definiuje (poprzez niejawną konwersję) <metodę: "daniel" <"chris" == false
Daniel Spiewak
11
Dla informacji, w scali 2.9.1 sort wydaje się przestarzały, użyj sortWith
Jérôme
5
val array = Array((for(i <- 0 to 10) yield scala.util.Random.nextInt): _*)
scala.util.Sorting.quickSort(array)

„Domyślna” tablica Scali to zmienna struktura danych, bardzo zbliżona do tablicy Javy. Mówiąc ogólnie, oznacza to, że „tablica” nie jest bardzo skalowalna, nawet jeśli występują zmienne struktury danych. Służy to jednak celowi. Jeśli tablica jest odpowiednim typem danych dla twoich potrzeb, to tak ją sortujesz. Nawiasem mówiąc, istnieją inne metody sortowania dotyczące sortowania obiektów.

Myślę, że właśnie zrozumiałem, jakie jest twoje pytanie ... nie musisz przekazywać żadnego niejawnego parametru (w końcu jest on niejawny). Ten parametr istnieje po to, aby powiedzieć, że musi istnieć sposób na przekształcenie typu K w Ordered [K]. Te definicje już istnieją dla klas Scali, więc ich nie potrzebujesz.

Dla dowolnej klasy możesz to zdefiniować w ten sposób:

scala> case class Person(name: String)
defined class Person

scala> val array = Array(Person("John"), Person("Mike"), Person("Abe"))
array: Array[Person] = Array(Person(John), Person(Mike), Person(Abe))

scala> scala.util.Sorting.quickSort(array)
<console>:11: error: no implicit argument matching parameter type (Person) => Ordered[Person] was found.
       scala.util.Sorting.quickSort(array)
                                   ^
scala> class OrderedPerson(val person: Person) extends Ordered[Person] {
     | def compare(that: Person) = person.name.compare(that.name)
     | }
defined class OrderedPerson

scala> implicit def personToOrdered(p: Person) = new OrderedPerson(p)
personToOrdered: (p: Person)OrderedPerson

scala> scala.util.Sorting.quickSort(array)

scala> array
res8: Array[Person] = Array(Person(Abe), Person(John), Person(Mike))

Teraz, gdyby osoba została zamówiona na początku, nie stanowiłoby to problemu:

scala> case class Person(name: String) extends Ordered[Person] {
     | def compare(that: Person) = name.compare(that.name)
     | }
defined class Person

scala> val array = Array(Person("John"), Person("Mike"), Person("Abe"))
array: Array[Person] = Array(Person(John), Person(Mike), Person(Abe))

scala>  scala.util.Sorting.quickSort(array)

scala> array
res10: Array[Person] = Array(Person(Abe), Person(John), Person(Mike))
Daniel C. Sobral
źródło
Przepraszam, nie było jasne: muszę posortować tablicę obiektów dowolnego typu, a nie liczb.
Dan Gravell,
3

Chociaż przyjęta odpowiedź nie jest błędna, metoda szybkiego sortowania zapewnia większą elastyczność. Napisałem dla Ciebie ten przykład.

import System.out.println
import scala.util.Sorting.quickSort

class Foo(x:Int) {
def get = x
}

//a wrapper around Foo that implements Ordered[Foo]
class OrdFoo(x:Foo) extends Ordered[Foo] {
def compare(that:Foo) = x.get-that.get
}
//another wrapper around Foo that implements Ordered[Foo] in a different way
class OrdFoo2(x:Foo) extends Ordered[Foo] {
def compare(that:Foo) = that.get-x.get
}
//an implicit conversion from Foo to OrdFoo
implicit def convert(a:Foo) = new OrdFoo(a)

//an array of Foos
val arr = Array(new Foo(2),new Foo(3),new Foo(1))

//sorting using OrdFoo
scala.util.Sorting.quickSort(arr)
arr foreach (a=>println(a.get))
/*
This will print:
1
2
3
*/

//sorting using OrdFoo2
scala.util.Sorting.quickSort(arr)(new OrdFoo2(_))
arr foreach (a=>println(a.get))
/*
This will print:
3
2
1
*/

To pokazuje, jak niejawne i jawne konwersje z Foo do niektórych rozszerzających klas klas Ordered [Foo] mogą być użyte do uzyskania różnych kolejności sortowania.

Kim Stebel
źródło
2

Wolę używać sortowania użytkownika

Przykład:

val arr = Array(7,5,1, 9,2)

scala.util.Sorting.quickSort(arr)

przeczytaj to, aby uzyskać więcej informacji Sortowanie util

Ahmad Al-Kurdi
źródło
Po co odsyłacz do starej dokumentacji interfejsu API w wersji 2.9.0 zamiast bieżącej ?
jwvh