2010-05-21 5 views
9

Có cách nào tốt hơn để làm điều này:Cách tốt nhất để ghi điểm và tổng hợp ở Scala?

val totalScore = set.foldLeft(0)(_ + score(_)) 

hay này:

val totalScore = set.toSeq.map(score(_)).sum 

Tôi nghĩ rằng nó khá một hoạt động phổ biến như vậy đã được mong đợi một cái gì đó kiểu dáng đẹp như:

val totalScore = set.sum(score(_)) 
+1

Hai đoạn mã đầu tiên làm những việc khác nhau. Xem bình luận của tôi cho câu trả lời của Daniel C. Sobral. – dsg

+0

Thanks @dsg, tôi đã thay đổi nó. – adam77

Trả lời

17

Vâng, có những cách khác để viết nó:

val totalScore = set.toSeq.map(score(_)).sum 
val totalScore = set.toSeq.map(score).sum 
val totalScore = set.toSeq map score sum 

Người cuối cùng có thể yêu cầu một dấu chấm phẩy ở cuối nếu dòng tiếp theo không bắt đầu bằng một từ khóa. Người ta cũng có thể sử dụng .view thay vì .toSeq, điều này sẽ tránh phân bổ bộ sưu tập tạm thời. Tuy nhiên, tôi không chắc chắn hành vi hiện tại của .view (của việc hiển thị các yếu tố lặp đi lặp lại) là chính xác.

+1

Điều này có vẻ như giải pháp đơn giản và phù hợp nhất. Tôi muốn đọc điều này thay vì có một hàm bao gồm thực hiện ánh xạ. – ziggystar

+3

Một mối quan tâm hợp pháp với cách tiếp cận hai bước có thể là hiệu suất - nó tạo ra một danh sách thứ hai chỉ để tổng hợp nó. Tuy nhiên, bạn có thể sử dụng chế độ xem để tránh chi phí này: 'set.view.map (điểm) .sum'. Việc tách biệt các mối quan tâm về ánh xạ và tổng hợp tránh được các phương thức thổi phồng trong thư viện chuẩn. Nếu bạn làm điều đó thường xuyên, bạn có thể thêm 'mapAndSum' vào mã của riêng bạn. – retronym

+0

Có ai có liên kết đến một số tài liệu về chế độ xem không? – adam77

1

đơn giản :

scala> val is1 = Set(1, 4, 9, 16) 
is1: scala.collection.immutable.Set[Int] = Set(1, 4, 9, 16) 
scala> is1.reduceLeft(_ + _) 
res0: Int = 30 

Với phương pháp điểm số của bạn:

scoreSet.reduceLeft(_ + score(_)) 

Cảnh báo, tuy nhiên, điều này không được các bộ sưu tập được giảm là trống rỗng trong khi lần không:

scala> val is0 = Set[Int]() 
is0: scala.collection.immutable.Set[Int] = Set() 

scala> is0.foldLeft(0)(_ + _) 
res1: Int = 0 
+3

Điều này sẽ không hoạt động. Loại bộ sưu tập khác với loại kết quả (đó là lý do cho cuộc gọi đến 'điểm số'), vì vậy' reduceLeft' không phải là một tùy chọn. –

1

Cách khác, sự quá tải Seq#sum mà phải mất một chuyển đổi ngầm để Numeric có thể được sử dụng nếu loại trong bộ sưu tập được ghi/tổng hợp không có một toán tử bổ sung. Tuy nhiên, vì nó là một tham số chuyển đổi tiềm ẩn, nó sẽ không được áp dụng trừ khi được yêu cầu để thực hiện kiểm tra loại đóng cửa giảm.

5

Seq.sum không có chức năng có thể được sử dụng để ghi tổng. Bạn có thể xác định một chuyển đổi ngầm mà "ma cô" Traversable:

implicit def traversableWithSum[A](t: Traversable[A])(implicit m: Numeric[A]) = new { 
    def sumWith(f: A => A) = t.foldLeft(m.zero)((a, b) => m.plus(a, f(b))) 
} 

def score(i: Int) = i + 1 

val s = Set(1, 2, 3) 

val totalScore = s.sumWith(score _) 
println(totalScore) 
=> 9 

Xin lưu ý rằng Numeric đặc điểm chỉ tồn tại trong Scala 2.8.

+2

Michel, tôi thích giải pháp của bạn và tôi nghĩ rằng tôi sẽ làm cho nó một chút chung chung hơn bằng cách cho phép điểm số để làm việc trên một đối tượng, như "Trò chơi" và trả về một số: ngầm traversableWithSum [A] (t: Traversable [A]) = new { def sumVới [B] (f: A => B) (ngầm định m: Numeric [B]): B = t.foldLeft (m.zero) ((a, b) => m .plus (a, f (b))) } – Eric