2013-02-25 69 views
19

Haskell hỗ trợ một số thao tác cơ bản để đệ quy thông qua danh sách, như head, tail, initlast. Tôi tự hỏi, nội bộ, Haskell đại diện cho dữ liệu danh sách của nó như thế nào? Nếu đó là danh sách được liên kết đơn lẻ, thì các hoạt động initlast có thể trở nên tốn kém khi danh sách tăng lên. Nếu đó là danh sách được liên kết kép, tất cả bốn hoạt động có thể được thực hiện O(1) khá dễ dàng, mặc dù chi phí của một số bộ nhớ. Dù bằng cách nào, điều quan trọng là tôi phải biết, vì vậy tôi có thể viết mã thích hợp. (mặc dù, các đặc tính của lập trình chức năng có vẻ là một trong những "hỏi những gì nó làm, không phải như thế nào nó").Trình bày nội bộ danh sách Haskell?

+1

"hỏi nó làm gì, không phải như thế nào" Không phải nếu bạn lo ngại về việc viết mã là hợp lý nhanh chóng;) –

+0

Vâng, đó là những gì tôi nghĩ :-) Do đó câu hỏi của tôi. –

+1

"Nếu đó là danh sách được liên kết gấp đôi, tất cả bốn hoạt động có thể được thực hiện O (1) khá dễ dàng", thật không dễ dàng nếu bạn muốn giữ nguyên chức năng, vì vậy các danh sách được liên kết gấp đôi bình thường không được sử dụng nhiều trong Haskell . Làm mọi thứ trong _O_ (1) trong khi hoàn toàn là chức năng đòi hỏi cấu trúc dữ liệu phức tạp hơn - tuy nhiên hóa ra, bằng cách khai thác sự lười biếng của Haskell, bạn có thể làm được nhiều hơn với các hoạt động _O_ (1) (hoặc theo cách nào đó được phân bổ _O_ (_n_), gần như là tốt) trên các danh sách liên kết đơn lẻ được liên kết của nó hơn là có thể bằng bất kỳ ngôn ngữ thủ tục nào. – leftaroundabout

Trả lời

28

Danh sách được trình bày dưới dạng ... danh sách được liên kết đơn lẻ. Định nghĩa được cho bởi:

data [] a = [] | a : [a] 

mà bạn có thể viết như sau:

data List a = Empty | Cons a (List a) 

Cách bố trí bộ nhớ được hoàn toàn xác định bởi đây.

  • Constructors là đống phân bổ
  • lĩnh vực đa hình nội bộ là con trỏ đến nút phân bổ khác
  • Cột sống là lười biếng

Vì vậy, bạn kết thúc với một cái gì đó như thế này:

enter image description here

Vì vậy, headO (1) trên cấu trúc này, trong khi last hoặc (++)O (n)

Không có phép thuật để cấu trúc dữ liệu trong Haskell - nét thẳng về phía trước của họ làm hoàn toàn rõ ràng những gì mức độ phức tạp sẽ được (modulo lười biếng). Nếu bạn cần sự phức tạp khác nhau, hãy sử dụng một cấu trúc khác (chẳng hạn như IntMap, Trình tự, HashMap, Vector v.v.) ...

+3

Cảm ơn câu trả lời.Tôi không chắc nó cần thiết để nhấn mạnh rõ ràng/rõ ràng câu trả lời này nên được - Tôi là một người mới bắt đầu Haskell, và đến từ C đó là một sự thay đổi rất lớn, vì vậy tôi vẫn tìm ra công cụ. Dù sao một lần nữa cảm ơn. –

+7

Ồ, tôi không có nghĩa là "dễ dàng" của nó, chỉ là không có ma thuật liên quan. Nếu bạn chỉ cần nhìn vào định nghĩa kiểu dữ liệu, thì tất cả đều có thể phát sinh được. –

+0

Hai cảnh báo lớn: ** lười biếng ** và ** tổng hợp **. Sự lười biếng có nghĩa là, ví dụ, trong 'xs ++ ys' bạn chỉ phải trả chi phí của phần phụ thêm vào phạm vi mà bạn duyệt qua danh sách kết quả; 'head (xs ++ ys)' là O (1), không phải O (n). Fusion có nghĩa là nhiều hoạt động không phải trả thêm chi phí nào cho quá trình truyền tải; ví dụ, 'map (* 2) (xs ++ ys)' chi phí ít hơn tổng chi phí của 'map (* 2)' và '++', bởi vì GHC loại bỏ danh sách trung gian được tạo ra. –

14

danh sách Haskell đang đơn lẻ liên kết, vì vậy cons, headtail là O (1) trong khi initlast là O (n ).

Nếu bạn cần hiệu suất tốt hơn, hãy cân nhắc sử dụng loại Seq từ Data.Sequence, cung cấp truy cập O (1) cho cả hai đầu của danh sách. Bên trong nó sử dụng 2-3 finger trees.

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