2012-04-13 22 views
19

Trường hợp sử dụng hiện tại của tôi khá tầm thường, Bản đồ có thể thay đổi hoặc không thay đổi sẽ thực hiện thủ thuật.Bản đồ bất biến của Scala, khi nào có thể thay đổi được?

Có một phương pháp mà phải mất một Bản đồ bất biến, mà sau đó gọi một phương thức API bên thứ 3 mà phải mất một Bản đồ bất biến cũng

def doFoo(foo: String = "default", params: Map[String, Any] = Map()) { 

    val newMap = 
    if(someCondition) params + ("foo" -> foo) else params 

    api.doSomething(newMap) 
} 

Các bản đồ trong câu hỏi thường sẽ khá nhỏ, ít nhất có thể có một danh sách nhúng của các cá thể lớp chữ hoa, một vài nghìn mục tối đa. Vì vậy, một lần nữa, giả sử ít tác động trong việc không thay đổi trong trường hợp này (tức là về cơ bản có 2 bản đồ của Bản đồ thông qua bản sao val bản đồ mới).

Tuy nhiên, nó hơi nhích tôi một chút, sao chép bản đồ chỉ để có được một bản đồ mới với một vài k-> v mục tacked vào nó.

Tôi có thể chuyển sang chế độ có thể thay đổi và params.put("bar", bar), v.v. cho các mục tôi muốn nhấn và sau đó params.toMap để chuyển đổi thành không đổi cho cuộc gọi api, đó là một tùy chọn. nhưng sau đó tôi phải nhập và vượt qua các bản đồ có thể thay đổi, điều này hơi phức tạp so với việc sử dụng Bản đồ bất biến mặc định của Scala.

Vì vậy, các nguyên tắc chung cho thời điểm nào là thực tiễn hợp lý/tốt để sử dụng Bản đồ có thể thay đổi trên Bản đồ bất biến?

Cảm ơn

EDIT như vậy, có vẻ như một add hoạt động trên bản đồ bất biến mất gần thời gian liên tục, xác nhận @ DHG và @ khẳng định Nicolas rằng một bản sao đầy đủ không được thực hiện, trong đó giải quyết vấn đề cho trường hợp cụ thể được trình bày.

+4

Xem [Cực thông minh: Cấu trúc dữ liệu chức năng trong Scala] (http://www.infoq.com/presentations/Functional-Data-Structures-in-Scala). –

+0

cảm ơn, xem ngay bây giờ – virtualeyes

Trả lời

29

Tùy thuộc vào việc triển khai Bản đồ bất biến, việc thêm một số mục có thể không thực sự sao chép toàn bộ Bản đồ ban đầu. Đây là một trong những ưu điểm của phương pháp cấu trúc dữ liệu bất biến: Scala sẽ cố gắng tránh xa việc sao chép càng ít càng tốt.

Loại hành vi này dễ thấy nhất với List. Nếu tôi có val a = List(1,2,3), thì danh sách đó được lưu trong bộ nhớ. Tuy nhiên, nếu tôi thêm một phần tử bổ sung như val b = 0 :: a, tôi làm lấy một phần tử 4 mới List trở lại, nhưng Scala đã làm không sao chép danh sách orignal a. Thay vào đó, chúng tôi vừa tạo một liên kết mới, được gọi là b và đưa con trỏ đến Danh sách a hiện tại.

Bạn cũng có thể hình dung các chiến lược như thế này cho các loại bộ sưu tập khác. Ví dụ: nếu tôi thêm một phần tử vào Map, bộ sưu tập có thể chỉ đơn giản là bọc bản đồ hiện tại, quay lại khi cần, tất cả trong khi cung cấp API như thể nó là một đơn Map.

+1

+1, thật tuyệt, không phải là vấn đề nếu đó là trường hợp. Chỉ làm phiền khi nghĩ đến việc tạo một bản sao đầy đủ khi cần thêm một vài k-> v vào bản đồ gốc – virtualeyes

+0

nó sẽ là thú vị để giải thích những bộ sưu tập bất biến nào mạnh mẽ hơn với những bổ sung và cái nào thì không. Tôi mong đợi ListMaps là một trong những điều đó sẽ không thực sự mạnh mẽ. –

5

Ngoài câu trả lời của dhg, bạn có thể xem qua số performance of the scala collections. Nếu thao tác thêm/xóa không mất thời gian tuyến tính, nó phải làm điều gì khác hơn là chỉ cần sao chép toàn bộ cấu trúc. (Lưu ý rằng trò chuyện không đúng: nó không phải là beacuase phải mất thời gian tuyến tính mà bạn sao chép toàn bộ cấu trúc)

+0

+1, liên kết tuyệt vời, cảm ơn. Theo Performance & Characteristics, một hoạt động thêm vào HashMap bất biến mất gần thời gian không đổi, trong trường hợp hiện tại, chỉ là những gì tôi muốn nghe ;-) – virtualeyes

14

Sử dụng một đối tượng có thể thay đổi không phải là xấu, nó trở nên xấu trong môi trường lập trình chức năng, nơi bạn cố gắng tránh tác dụng phụ bằng cách giữ chức năng tinh khiết và đối tượng không thay đổi được.

Tuy nhiên, nếu bạn tạo đối tượng có thể thay đổi bên trong một hàm và sửa đổi đối tượng này, hàm vẫn tinh khiết nếu bạn không giải phóng tham chiếu đến đối tượng này bên ngoài hàm. Có thể chấp nhận có mã như:

def buildVector(x: Double, y: Double, z: Double): Vector[Double] = { 
    val ary = Array.ofDim[Double](3) 
    ary(0) = x 
    ary(1) = y 
    ary(2) = z 
    ary.toVector 
} 

Bây giờ, tôi nghĩ rằng phương pháp này là hữu ích/đề nghị trong hai trường hợp: (1) Hiệu suất, nếu tạo và chỉnh sửa một đối tượng bất biến là một nút cổ chai của toàn bộ ứng dụng của bạn; (2) Khả năng đọc mã, bởi vì đôi khi việc sửa đổi một đối tượng phức tạp dễ dàng hơn (thay vì sử dụng ống kính, dây kéo, v.v.)

+0

Có, thay thế để ở với mặc định bất biến được truyền trong một bản đồ có thể thay đổi , sau đó chuyển đổi sang bản đồ bất biến trước khi thực hiện cuộc gọi api, do đó không hoạt động thuần túy so với cách tiếp cận do-it-all-local của bạn. Nó có vẻ như cho trường hợp chung, không cực đoan, mặc định bất biến là tốt. Tất nhiên, tôi vẫn chưa rõ ràng về các trường hợp sử dụng cụ thể cho bản đồ có thể thay đổi không thay đổi được. Nên để cánh cửa mở bằng cách không cung cấp trường hợp tầm thường khiến tôi đặt câu hỏi ... – virtualeyes

+0

+1: Về cơ bản, mẫu "builder" được sử dụng bởi các bộ sưu tập bất biến BCL của Microsoft (https: //www.nuget. org/packages/Microsoft.Bcl.Immutable) cho .NET. –

0

Tôi thích sử dụng collection.maps như các kiểu tham số được khai báo (giá trị đầu vào hoặc trả về)) thay vì bản đồ có thể thay đổi hoặc không thay đổi được. Bản đồ Bộ sưu tập là các giao diện bất biến hoạt động cho cả hai loại triển khai. Một phương pháp tiêu dùng bằng cách sử dụng một bản đồ thực sự không cần phải biết về việc thực hiện bản đồ hoặc cách nó được xây dựng. (Nó thực sự không phải của nó kinh doanh anyway).

Nếu bạn đi với cách tiếp cận ẩn bản đồ cụ thể của bản đồ (có thể thay đổi hoặc bất biến) từ những người tiêu dùng sử dụng nó thì bạn vẫn nhận được bản đồ hạ lưu bản đồ không thay đổi. Và bằng cách sử dụng collection.Map như một giao diện bất biến, bạn hoàn toàn loại bỏ tất cả các ". Map" không hiệu quả mà bạn sẽ có với người tiêu dùng bằng văn bản để sử dụng các đối tượng được nhập không thay đổi. Có để chuyển đổi một bản đồ hoàn toàn được xây dựng thành một bản đồ khác chỉ đơn giản là tuân thủ một giao diện không được hỗ trợ bởi giao diện đầu tiên thực sự là phí tổn không cần thiết khi bạn nghĩ về nó. Tôi nghi ngờ trong một vài năm tới, chúng tôi sẽ xem xét ba bộ giao diện riêng biệt (bản đồ có thể thay đổi, bản đồ bất biến và bản đồ bộ sưu tập) và nhận ra rằng 99% thời gian chỉ có 2 thực sự cần thiết (có thể thay đổi được) và các bộ sưu tập) và việc sử dụng giao diện bản đồ bất biến mặc định (không may) thực sự làm tăng thêm rất nhiều chi phí không cần thiết cho "Ngôn ngữ có thể mở rộng".

Các vấn đề liên quan