2010-02-03 3 views
26

Trong Scala 2.8, tôi có nhu cầu gọi List.min và cung cấp chức năng so sánh của riêng tôi để lấy giá trị dựa trên phần tử thứ hai của Tuple2. Tôi đã phải viết loại code này:Trong Scala, cách sử dụng Đặt hàng [T] với List.min hoặc List.max và giữ mã có thể đọc được

val list = ("a", 5) :: ("b", 3) :: ("c", 2) :: Nil 

list.min(new Ordering[Tuple2[String,Int]] { 
    def compare(x:Tuple2[String,Int],y:Tuple2[String,Int]): Int = x._2 compare y._2 
}) 

Có cách nào để làm điều này dễ đọc hơn hoặc để tạo ra một Thứ tự ra khỏi một chức năng ẩn danh như bạn có thể làm với list.sortBy(_._2)?

Trả lời

29

Thưa các bạn, bạn đã làm cho người hỏi khó tìm "tự". Hiệu suất khá tồi tàn. Bạn có thể cạo thêm một chút để viết nó như thế này:

list min Ordering[Int].on[(_,Int)](_._2) 

Điều gì vẫn còn quá ồn ào nhưng đó là nơi chúng tôi đang ở thời điểm này.

+6

'danh sách min Ordering.by ((_: ​​(_, Int)) ._ 2)' –

+11

Tôi thấy mặt cười ... – soc

+0

danh sách min Đặt hàng [Int] .on [WeightedType] (_. Weight) – cessationoftime

10

Một điều bạn có thể làm là sử dụng cú pháp loại tuple tiêu chuẩn ngắn gọn hơn thay vì sử dụng Tuple2:

val min = list.min(new Ordering[(String, Int)] { 
    def compare(x: (String, Int), y: (String, Int)): Int = x._2 compare y._2 
}) 

Hoặc sử dụng reduceLeft để có một giải pháp ngắn gọn hơn hẳn:

val min = list.reduceLeft((a, b) => (if (a._2 < b._2) a else b)) 

Hoặc bạn có thể sắp xếp danh sách theo tiêu chí của bạn và nhận được phần tử first (hoặc last cho tối đa):

val min = list.sort((a, b) => a._2 < b._2).first 

Mà có thể được rút ngắn hơn nữa bằng cách sử dụng cú pháp giữ chỗ:

val min = list.sort(_._2 < _._2).first 

nào, như bạn đã viết cho mình, có thể được rút ngắn xuống còn:

val min = list.sortBy(_._2).first 

Nhưng như bạn đề nghị sortBy mình, tôi không chắc chắn nếu bạn đang tìm kiếm một cái gì đó khác nhau ở đây.

+2

Tôi chưa xem mã thư viện, nhưng lý do tôi thích sử dụng 'min' là tôi giả định rằng nó sẽ là tuyến tính trong thời gian trong khi' sortBy' sẽ là o (n.log (n)). Tôi cũng nghĩ rằng việc sử dụng một phương thức gọi là 'min' làm cho intent rõ hơn, mặc dù' list.sortBy (_._ 2) .first' cũng khá rõ ràng. – huynhjl

3

Bạn luôn có thể xác định chuyển đổi ngầm của riêng bạn:

implicit def funToOrdering[T,R <% Ordered[R]](f: T => R) = new Ordering[T] { 
    def compare(x: T, y: T) = f(x) compare f(y) 
} 

val list = ("a", 5) :: ("b", 3) :: ("c", 2) :: Nil 

list.min { t: (String,Int) => t._2 } // (c, 2) 

EDIT: mỗi @ comments Dario của.

Có thể dễ đọc hơn nếu việc chuyển đổi là không tiềm ẩn, nhưng sử dụng một "on" chức năng:

def on[T,R <% Ordered[R]](f: T => R) = new Ordering[T] { 
    def compare(x: T, y: T) = f(x) compare f(y) 
} 

val list = ("a", 5) :: ("b", 3) :: ("c", 2) :: Nil 

list.min(on { t: (String,Int) => t._2 }) // (c, 2) 
+0

+1 Rất hay. Đã chuẩn bị đăng bài tương tự. Trong Haskell, bạn có một bộ phối hợp 'on' được định nghĩa cho mục đích này. Ví dụ. 'sắp xếp (so sánh 'trên' độ dài)' để sắp xếp danh sách theo độ dài – Dario

+0

Vâng, tôi thích chuyển đổi rõ ràng hơn. Đọc tốt hơn với "bật". –

+0

Bí quyết là về cơ bản là một nhà điều hành infix cho các chức năng. Chỉ là 'f 'trên' g = \ x y -> f (g x) (g y)' cho tất cả các hàm phù hợp. Nhưng bạn cũng tốt đẹp như vậy – Dario

5
list.min(Ordering.fromLessThan[(String, Int)](_._2 < _._2)) 

Đó là vẫn còn quá rườm rà, tất nhiên. Tôi có thể tuyên bố nó là val hoặc object.

6

Chức năng Ordering#on chứng kiến ​​thực tế là Ordering là một hàm giả biến đối ứng. Những người khác bao gồm Comparator, Function1, Comparablescalaz.Equal.

Scalaz cung cấp một cái nhìn thống nhất về các loại, vì vậy đối với bất kỳ trong số họ, bạn có thể thích ứng với đầu vào với value contramap f, hoặc với ngoại diên tượng trưng, ​​value ∙ f

scala> import scalaz._ 
import scalaz._ 

scala> import Scalaz._ 
import Scalaz._ 

scala> val ordering = implicitly[scala.Ordering[Int]] ∙ {x: (_, Int) => x._2} 
ordering: scala.math.Ordering[Tuple2[_, Int]] = [email protected] 

scala> List(("1", 1), ("2", 2)) min ordering 
res2: (java.lang.String, Int) = (1,1) 

Đây là việc chuyển đổi từ các Ordering[Int] để Ordering[(_, Int)] chi tiết hơn :

scala> scalaz.Scalaz.maContravariantImplicit[Ordering, Int](Ordering.Int).contramap { x: (_, Int) => x._2 } 
res8: scala.math.Ordering[Tuple2[_, Int]] = [email protected] 
+2

Scalaz là cấp độ đai đen. Có thể nguy hiểm cho tôi để thử ngay bây giờ ... Nhưng nó có thể cung cấp cho tôi một góc độ về Scalaz mà tôi có thể liên quan đến. Vì vậy, quan điểm thống nhất được cung cấp bởi 'ngầm '? 'Comap' là gì? Nó tương tự như 'map'? Tôi có thể tìm thêm tài liệu ở đâu? – huynhjl

+2

Cập nhật câu trả lời theo Scalaz 6. – missingfaktor

32

Trong Scala 2.9, bạn có thể làm list minBy { _._2 }.

+0

Cần lưu ý rằng kỹ thuật được đề cập trong câu trả lời của @ retronym có tính tổng hợp hơn nhiều, và nên được ưu tiên nói chung. – missingfaktor