Giải pháp cho việc triển khai các hoạt động trên các cấu trúc dữ liệu liên kết đôi hoặc liên kết trong các ngôn ngữ có dữ liệu không thay đổi


11

Tôi muốn tìm hiểu cách tạo biểu đồ và thực hiện một số thao tác cục bộ trên chúng trong Haskell, nhưng câu hỏi không dành riêng cho Haskell, và thay vì biểu đồ, chúng tôi có thể xem xét các danh sách được liên kết đôi.

Câu hỏi: Điều gì sẽ là một cách thành ngữ hoặc được đề xuất để thực hiện danh sách liên kết đôi (hoặc cấu trúc dữ liệu liên kết đôi hoặc liên kết đôi khác) và hoạt động trên ngôn ngữ đó chủ yếu hỗ trợ và ủng hộ các cấu trúc dữ liệu bất biến (Haskell, Clojure, v.v.) ? Cụ thể, làm thế nào để sử dụng các bản cập nhật tại chỗ, bị chính thức cấm bởi ngôn ngữ?

Tôi có thể dễ dàng tưởng tượng rằng nếu một số thao tác cục bộ được thực hiện trong danh sách được liên kết đôi (chẳng hạn nếu một mục được chèn), thì có thể không cần phải sao chép toàn bộ danh sách ngay lập tức do sự lười biếng của ngôn ngữ. Tuy nhiên, vì danh sách được liên kết đôi, nếu nó được sửa đổi ở một nơi, không có nút nào trong số các nút cũ có thể được sử dụng trong phiên bản mới của danh sách và chúng sẽ cần được đánh dấu, sao chép, thu gom rác sớm hay muộn . Rõ ràng đây là những hoạt động dư thừa nếu chỉ sử dụng bản sao cập nhật của danh sách, nhưng chúng sẽ thêm một "chi phí" tỷ lệ thuận với kích thước của danh sách.

Điều này có nghĩa là đối với các tác vụ như vậy, dữ liệu bất biến chỉ đơn giản là không phù hợp và các ngôn ngữ khai báo chức năng không có hỗ trợ "gốc" cho dữ liệu có thể thay đổi sẽ không tốt như dữ liệu bắt buộc? Hoặc, có một số cách giải quyết khó khăn?

Tái bút: Tôi đã tìm thấy một số bài báo và bài thuyết trình về chủ đề này trên Internet nhưng gặp khó khăn khi theo dõi chúng, trong khi tôi nghĩ rằng câu trả lời cho câu hỏi này không nên mất nhiều hơn một đoạn và có thể là một sơ đồ ... Ý tôi là, nếu có không có giải pháp "chức năng" cho vấn đề này, câu trả lời có lẽ là "sử dụng C". Nếu có một, thì nó có thể phức tạp như thế nào?


Câu hỏi liên quan

  • "Cấu trúc dữ liệu trong lập trình chức năng" . Câu hỏi cụ thể của tôi về việc sử dụng các bản cập nhật tại chỗ thay vì các giải pháp thay thế không hiệu quả không được thảo luận ở đó.

  • "Đột biến nội bộ của cấu trúc dữ liệu liên tục" . Có sự nhấn mạnh về việc triển khai ở mức độ thấp trong một ngôn ngữ không xác định, trong khi câu hỏi của tôi là về sự lựa chọn đúng đắn của ngôn ngữ (chức năng hoặc cách khác) và về các giải pháp thành ngữ có thể có trong các ngôn ngữ chức năng.


Trích dẫn có liên quan

Các ngôn ngữ lập trình chức năng thuần túy cho phép nhiều thuật toán được thể hiện rất chính xác, nhưng có một vài thuật toán trong đó trạng thái cập nhật tại chỗ dường như đóng một vai trò quan trọng. Đối với các thuật toán này, các ngôn ngữ hoàn toàn chức năng, thiếu trạng thái cập nhật, dường như không hiệu quả ( [Ponder, McGeer và Ng, 1988] ).

- John Launchbury và Simon Peyton Jones, chủ đề nhà nước chức năng lười biếng (1994), cũng là John Launchbury và Simon Peyton Jones, State in Haskell (1995). Các bài viết này giới thiệu STcác nhà xây dựng loại đơn trong Haskell.


4
Đề xuất: Okasaki
Robert Harvey

2
Cảm ơn đã tham khảo. Tôi đã tìm thấy luận án của mình .
Alexey

Bài viết này có vẻ đầy hứa hẹn: Các thuật toán đồ thị tuyến tính và tìm kiếm sâu đầu tiên trong Haskell (1994), của David King và John Launchbury.
Alexey

Có vẻ như một vấn đề tương tự với các mảng được giải quyết bằng gói diffarray thực hiện DiffArraykiểu. Nhìn vào nguồn của gói diffarray , tôi thấy 91 lần xuất hiện unsafePerformIO. Có vẻ như câu trả lời cho câu hỏi của tôi là "có, không, các ngôn ngữ chức năng thuần túy với dữ liệu bất biến không phù hợp để thực hiện các thuật toán thường dựa vào các bản cập nhật tại chỗ".
Alexey

Giải pháp hiện tại của tôi (trong Haskell) là sử dụng một cuốn từ điển ( Map, IntMaphoặc HashMap) như là một lưu trữ và để làm cho các nút chứa ID của các nút liên kết. "Tất cả các vấn đề trong khoa học máy tính có thể được giải quyết bằng một mức độ gián tiếp khác."
Alexey

Câu trả lời:


6

Có thể có các cấu trúc dữ liệu bất biến hiệu quả khác phù hợp với nhiệm vụ cụ thể của bạn, nhưng không chung chung như một danh sách liên kết đôi (không may bị lỗi sửa đổi đồng thời do tính biến đổi của nó). Nếu bạn chỉ định vấn đề của mình hẹp hơn, cấu trúc như vậy có thể được tìm thấy.

Câu trả lời chung cho (tương đối) kinh tế của các cấu trúc bất biến là thấu kính. Ý tưởng là bạn có thể giữ vừa đủ thông tin để tái cấu trúc một cấu trúc bất biến đã sửa đổi từ các phần chưa được sửa đổi của nó và phần hiện đang được sửa đổi, và điều hướng qua nó đến một nút lân cận.

Một cấu trúc hữu ích khác là một dây kéo . (Phần buồn cười là chữ ký loại cho khóa kéo ống kính là một dẫn xuất toán học của chữ ký loại của cấu trúc.)

Đây là một số đường dẫn.


1
tùy thuộc vào những gì cần thiết, khóa kéo cũng có thể hữu ích
jk.

Để xác định vấn đề của tôi hẹp hơn, giả sử tôi muốn lập trình một hệ thống viết lại biểu đồ, ví dụ như một công cụ đánh giá tính toán lambda dựa trên việc viết lại biểu đồ.
Alexey

1
@Alexey: Bạn có quen thuộc với công việc của những người Sạch trên viết lại biểu đồ không? wiki.clean.cs.ru.nl/ trộm
Giorgio

1
@Alexey: Không phải tôi biết: Clean là anh em họ của Haskell được phát triển riêng. Nó cũng có một cơ chế khác để xử lý các tác dụng phụ (AFAIK, nó được gọi là các loại duy nhất). Mặt khác, các nhà phát triển đã làm việc rất nhiều với việc viết lại biểu đồ. Vì vậy, họ có thể là một trong những người giỏi nhất biết cả về viết lại đồ thị và lập trình chức năng.
Giorgio

1
Tôi đồng ý rằng khóa kéo dường như giải quyết vấn đề với danh sách liên kết đôi hoặc cây nếu tôi muốn điều hướng và sửa đổi tại vị trí tôi hiện đang ở, nhưng không rõ phải làm gì nếu tôi muốn tập trung vào một số điểm đồng thời và, ví dụ, trao đổi hai yếu tố ở hai nơi cách xa nhau. Nó thậm chí còn ít rõ ràng hơn nếu có thể được sử dụng với các cấu trúc "tròn".
Alexey

2

Haskell không ngăn chặn việc sử dụng các cấu trúc dữ liệu có thể thay đổi. Chúng được khuyến khích rất nhiều và khó sử dụng hơn vì thực tế là các phần mã sử dụng chúng cuối cùng phải trả về một hành động IO (cuối cùng phải được ràng buộc vào hành động IO được trả về bởi hàm chính), nhưng điều đó không làm cho nó không thể sử dụng các cấu trúc như vậy nếu bạn thực sự cần chúng.

Tôi sẽ đề nghị điều tra việc sử dụng bộ nhớ giao dịch phần mềm như một cách chuyển tiếp. Cũng như cung cấp một cách hiệu quả để thực hiện các cấu trúc có thể thay đổi, nó cũng cung cấp các đảm bảo rất hữu ích cho an toàn luồng. Xem mô tả mô-đun tại https://hackage.haskell.org/package/stm và tổng quan về wiki tại https://wiki.haskell.org/Software_transactional_memory .


Cảm ơn, tôi sẽ cố gắng tìm hiểu về STM. Dường như có nhiều phương pháp trong Haskell có mutability và nhà nước (tôi đã tình cờ khi MVar, State, ST), vì vậy tôi sẽ cần phải tìm ra sự khác biệt của họ và có ý định sử dụng.
Alexey

@Alexey: Điểm hay về STIMO, nó nên được đề cập trong câu trả lời vì nó cho phép chạy một tính toán trạng thái, sau đó vứt bỏ trạng thái và trích xuất kết quả dưới dạng giá trị thuần túy.
Giorgio

@Giorgio, có thể sử dụng Haskell's STvới STM để có cả trạng thái đồng thời và trạng thái dùng một lần không?
Alexey

Chỉ cần thêm một gợi ý thuật ngữ: hành động IO chính được cấu thành không phải là "được trả về bởi hàm chính" mà được gán cho mainbiến. :) ( mainthậm chí không giữ chức năng.)
Alexey

Tôi thấy quan điểm của bạn, nhưng "biến" vẫn có ý nghĩa trong suy nghĩ của hầu hết mọi người như là một giá trị đơn giản, thay vì một quá trình tạo ra một giá trị, và chính rõ ràng là suy nghĩ tốt hơn là cái sau hơn là cái trước. Sự thay đổi mà bạn đề xuất, trong khi rõ ràng về mặt kỹ thuật, có khả năng gây nhầm lẫn cho những người không quen thuộc với chủ đề này.
Jules
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.