Các cấu trúc dữ liệu nên được tích hợp vào ngôn ngữ (như trong Python) hoặc được cung cấp trong thư viện chuẩn (như trong Java)?


21

Trong Python và rất có thể là nhiều ngôn ngữ lập trình khác, các cấu trúc dữ liệu phổ biến có thể được tìm thấy như một phần tích hợp của ngôn ngữ cốt lõi với cú pháp chuyên dụng của riêng chúng. Nếu chúng ta đặt cú pháp danh sách tích hợp của LISP sang một bên, tôi không thể nghĩ ra bất kỳ ngôn ngữ nào khác mà tôi biết cung cấp một số loại cấu trúc dữ liệu trên mảng như một phần tích hợp của cú pháp của chúng, mặc dù tất cả chúng (nhưng C, tôi đoán vậy) dường như cung cấp chúng trong thư viện tiêu chuẩn.

Từ quan điểm thiết kế ngôn ngữ, ý kiến ​​của bạn về việc có một cú pháp cụ thể cho cấu trúc dữ liệu trong ngôn ngữ cốt lõi là gì? Đó có phải là một ý tưởng tốt, và mục đích của ngôn ngữ (vv) có thay đổi mức độ tốt của sự lựa chọn này không?

Chỉnh sửa: Tôi xin lỗi vì (rõ ràng) gây ra một số nhầm lẫn về ý nghĩa cấu trúc dữ liệu nào. Tôi nói về những cái cơ bản và thường được sử dụng, nhưng vẫn không phải là những cái cơ bản nhất. Điều này không bao gồm cây (quá phức tạp, không phổ biến), ngăn xếp (quá hiếm khi được sử dụng), mảng (quá đơn giản) nhưng bao gồm các tập hợp, danh sách và hashmap.


1
Có phải chúng ta loại trừ đối tượng và hashmap?
Orble

3
@Anto: Rất nhiều ngôn ngữ có hashtag dưới dạng các mảng kết hợp, Perl, PHP, JS (về mặt kỹ thuật là một đối tượng ở đây), v.v.
Orble

1
Có lẽ bạn có thể cụ thể hơn về cấu trúc dữ liệu mà bạn nghĩ đến, ngoài các mảng, danh sách, hashmap / mảng kết hợp?
Thất vọngWithFormsDesigner

1
Bao gồm hashmap, danh sách và bất cứ thứ gì cao cấp hơn như "cấu trúc dữ liệu phức tạp" và loại bỏ các mảng quá đơn giản.
Anto

1
Tôi nghĩ một tiêu đề hợp lý hơn sẽ là một cái gì đó như: "Những cấu trúc dữ liệu nào nên được đưa vào ngôn ngữ, và những gì trong thư viện?" Một câu trả lời có ý nghĩa phụ thuộc rất nhiều vào ngôn ngữ: thư viện càng được tích hợp vào ngôn ngữ càng sạch thì việc chuyển các cấu trúc vào thư viện càng hợp lý.
Jerry Coffin

Câu trả lời:


13

Nó phụ thuộc vào những gì ngôn ngữ là dành cho.

Một số ví dụ (phần nào bị đánh cắp từ các câu trả lời khác):

  • Perl có cú pháp đặc biệt cho hashtables, mảng, chuỗi. Perl thường được sử dụng để viết kịch bản, những thứ này rất hữu ích cho việc viết kịch bản.
  • Matlab có cú pháp đặc biệt cho danh sách, ma trận, cấu trúc. Matlab là để làm ma trận và toán học véc tơ cho kỹ thuật.
  • Chuỗi và mảng hỗ trợ Java / .NET. Đây là những ngôn ngữ có mục đích chung nơi các mảng và chuỗi thường được sử dụng (ngày càng ít sử dụng các lớp bộ sưu tập mới)
  • Mảng hỗ trợ C / C ++. Đây là những ngôn ngữ không che giấu phần cứng từ bạn. Chuỗi được hỗ trợ một phần (không nối, sử dụng strcpy, v.v.)

Tôi nghĩ nó phụ thuộc vào mục đích / tinh thần / đối tượng ngôn ngữ của bạn là gì; trừu tượng như thế nào và bao xa từ phần cứng bạn muốn nó được. Nói chung, các ngôn ngữ hỗ trợ danh sách dưới dạng nguyên thủy cho phép bạn tạo danh sách dài vô hạn. Mặc dù mức độ thấp như C / C ++ sẽ không bao giờ có những thứ này, bởi vì đó không phải là mục tiêu, tinh thần của những ngôn ngữ đó.

Đối với tôi, bộ sưu tập rác tuân theo cùng một logic: khán giả của ngôn ngữ của bạn có quan tâm đến việc biết chính xác khi nào và nếu bộ nhớ được phân bổ hoặc giải phóng không? Nếu có, malloc / miễn phí; nếu không thì thu gom rác.


6
Đây là một nơi không tốt để sử dụng thuật ngữ "C / C ++", bởi vì sự hiện diện của các loại mẫu mức cao trong C ++ là một sự khác biệt lớn giữa hai ngôn ngữ.
dan04

Việc thu gom rác có thể được thực hiện theo kiểu xác định, bạn chỉ cần các loại tuyến tính (hoặc người thay thế người nghèo của họ: RAII).
pyon

@ EduardoLeón, mặc dù bạn có thể gọi bộ sưu tập rác tại một điểm xác định, tôi không nghĩ nó sẽ kéo dài trong bao lâu là xác định (vì lý do tương tự mallocnewkhông mang tính quyết định trong C / C ++).
Earl Namless

@earlNamless: Nó có tính xác định liên quan đến việc sử dụng tài nguyên: các loại tuyến tính (hoặc các loại duy nhất, tương tự nhau) làm cho nó trở thành một lỗi loại (và do đó, lỗi biên dịch) để không giải phóng tài nguyên (khả năng modulo, không bị bắt bởi loại hệ thống, của bất kỳ chấm dứt chương trình bất thường), hoặc sử dụng chúng sau khi chúng đã được xử lý.
pyon

5

Perl có hashmap và PL / SQL hỗ trợ các bản ghi và tôi có những ký ức rất mờ về matlab có cú pháp để hỗ trợ các vectơ và ma trận của tất cả các kích thước khác nhau (mặc dù tôi có thể sai về điều này và có thể tranh luận rằng đây là các loại dữ liệu không phải là dữ liệu cấu trúc ) ... Tôi muốn nói rằng có một số hỗ trợ riêng cho các cấu trúc rất phổ biến là tốt để có. Thông thường có vẻ như các mảng và hashmap / mảng kết hợp là các cấu trúc được hỗ trợ nguyên bản phổ biến nhất và có lẽ chúng cũng được sử dụng phổ biến nhất.

Đừng quên rằng nếu bạn thêm hỗ trợ cú pháp riêng cho các cấu trúc khác như cây nhị phân, các cấu trúc đó cũng đã được thực hiện bởi các công cụ hỗ trợ của ngôn ngữ (trình biên dịch / thời gian chạy / vv). Bạn muốn xây dựng bao nhiêu tầng lớp?

Bạn sẽ phải phát minh ra ký hiệu mới cho các cấu trúc ít được hỗ trợ hơn ... Giữ cho nó đơn giản!.


Không cần phải phát minh ra một cú pháp theo nghĩa đen cho ví dụ như cây - chúng hiếm hơn, chúng thậm chí không nằm trong tiêu chuẩn của nhiều ngôn ngữ! Với cùng một lý lẽ, người ta có thể phản đối việc đưa vào các toán tử vì "bạn phải phát minh ra ký hiệu mới cho các hoạt động ít được sử dụng hơn".

@delnan: Cách tôi hiểu là từ quan điểm thiết kế ngôn ngữ mới và tự hỏi liệu cấu trúc dữ liệu bên cạnh các mảng có nên được hỗ trợ bởi cú pháp mới (có thể) hay nếu chúng nên được hỗ trợ bằng cách bao gồm một thư viện.
Thất vọngWithFormsDesigner

Chà, câu đầu tiên nói rõ ràng về "cấu trúc dữ liệu chung", vì vậy tôi cho rằng OP không đủ điên rồ để thử thêm cú pháp đặc biệt cho mọi cấu trúc dữ liệu tối nghĩa từng được phát minh.

@delnan: ... và sau đó OP tiếp tục loại trừ các danh sách và mảng LISP (nói chung) "... đặt cú pháp danh sách tích hợp của LISP sang một bên, tôi không thể nghĩ ra bất kỳ ngôn ngữ nào khác mà tôi biết cung cấp một số loại cấu trúc dữ liệu phía trên mảng như là một phần tích hợp của cú pháp của họ "... vì vậy tôi nghĩ rằng họ đang cân nhắc các cấu trúc dữ liệu kỳ lạ hơn các mảng / danh sách ...
FrustratedWithFormsDesigner

Có (tôi đã hiểu "bên trên các mảng" là "các cấu trúc dữ liệu phổ biến khác"), nhưng không có gì trong gợi ý câu hỏi tại "hãy tạo ra chữ cho mỗi cấu trúc dữ liệu duy nhất chúng ta có". Thật tốt khi nói rằng điều này nên được giới hạn ở những gì hợp lý, nhưng tôi không nghĩ chúng ta có thể nói "ý tưởng tồi" chỉ vì giả định này .

5

Ví dụ yêu thích của tôi ở đây là Lua . Lua chỉ có một loại dữ liệu tích hợp, " bảng ", nhưng tính linh hoạt và tốc độ của nó có nghĩa là bạn thực sự sử dụng chúng thay cho các mảng thông thường, danh sách được liên kết, hàng đợi, bản đồ và thậm chí chúng là cơ sở cho các tính năng hướng đối tượng của Lua (tức là các lớp học).

Lua là một ngôn ngữ đơn giản đáng kinh ngạc như vậy, nhưng tính linh hoạt của cấu trúc dữ liệu bảng làm cho nó khá mạnh mẽ.


2
Các đối tượng JavaScript thực sự giống nhau - Mảng chỉ là các đối tượng thực sự có thuộc tính số và độ dài chẳng hạn.
Tikhon Jelvis

1
Các bảng Lua khác với các Đối tượng JavaScript: Trong JavaScript {}thì không [], trong Lua bạn có {}cả hai. Bảng Lua tốt hơn so với các danh sách trong Lisp.
Jakob

Tôi đoán trong JavaScript, "mọi thứ đều là một đối tượng" - bao gồm các mảng - nhưng không phải mọi thứ đều là một mảng. Ở Lua, mọi thứ đều là một cái bàn.
Dean Harding

3

Bạn không cần phải có cú pháp dành riêng cho mọi loại dữ liệu cấp cao. Ví dụ: có thể chấp nhận được set([1, 2, 3])(như Python 2.x đã làm) thay vì {1, 2, 3}.

Điều quan trọng là có một số cách thuận tiện để xây dựng cấu trúc dữ liệu cấp cao. Những gì bạn muốn tránh là mã như:

s = set()
s.add(1)
s.add(2)
s.add(3)

mà làm phiền tôi rất nhiều khi tôi sử dụng std::vector, std::setstd::maptrong C ++. Rất may, tiêu chuẩn mới sẽ có std::initializer_list.


3

Theo tôi, đó là một bổ sung đơn giản đáng kinh ngạc có thể trở nên tiện dụng một cách đáng ngạc nhiên thường xuyên, ít nhất là nếu được thực hiện một cách thận trọng - tức là nhiều nhất cho các bộ dữ liệu, danh sách, bản đồ và bộ như những người có chữ nghĩa được công nhận.

  • Thật rẻ khi thêm vào một ngôn ngữ. Nó không làm bạn tốn nhiều ngân sách phức tạp quý giá đó:
    • ngữ pháp về cơ bản là someBracket {expr ','} someBrackethoặc someBracket {expr ':' expr ','} someBracket, với một số bổ sung đơn giản đã chết nếu bạn muốn những thứ như dấu phẩy tùy chọn. Các chữ nổi có thể dễ dàng dài hơn trong ngữ pháp.
    • Trong nhiều ngôn ngữ, không có ngôn ngữ phổ biến nào đụng độ với cú pháp hiện có (một ngoại lệ tôi có thể nghĩ là một ngôn ngữ có các khối giống như dấu ngoặc như biểu thức, toán tử dấu phẩy và không có dấu chấm phẩy, như trong {1, 2})
    • Các ngữ nghĩa có thể được định nghĩa trong ít hơn năm câu, phiên bản không chính thức là "Khởi tạo một bộ sưu tập $ mới, sau đó gọi .add/ .append/ .setItemmột lần cho mỗi biểu thức đã cho với (các) biểu thức đó làm đối số".
  • Do điểm thứ ba trước đó, nó cũng rất dễ thực hiện.
  • Nó rất hữu ích khi bạn cần, và không (cần) tác động đến cú pháp của các yếu tố khác, tức là bạn không "trả tiền" cho nó khi bạn không sử dụng nó.

3

Clojure là một lisp nhưng hỗ trợ

Lists: (x1 x2)
Vectors: [x1 x2]
Maps: {k1 v1 k2 v2}
Sets: #{x1 x2}

2

Càng có nhiều cấu trúc dữ liệu trong ngôn ngữ, ngôn ngữ sẽ càng khó học hơn. Nó có thể là một sở thích cá nhân nhưng tôi có xu hướng thích một ngôn ngữ đơn giản hơn và sau đó bất kỳ tính năng bổ sung nào cũng có thể được cung cấp bởi các thư viện.

Các ngôn ngữ được thiết kế cho các trường cụ thể đôi khi có thể có lợi từ việc có các cấu trúc dữ liệu nhất định được tích hợp vào ngôn ngữ như Matlab. Nhưng quá nhiều có thể áp đảo bạn.


2

Để một ngôn ngữ thực sự hữu ích, nó phải thực hiện một mức độ nhất định của các nhiệm vụ. Bởi vì lập trình hàng ngày thực tế đòi hỏi các công cụ giải quyết vấn đề của họ ở một mức độ chung nào đó. Minimalism trông nhỏ gọn và mát mẻ nhưng khi bạn muốn bắt đầu sử dụng để giải quyết các vấn đề lớn nhưng lặp đi lặp lại, bạn cần một mức độ trừu tượng trên đỉnh mà bạn có thể xây dựng.

Vì vậy, tôi nghĩ rằng các ngôn ngữ lập trình nên hỗ trợ cho các cấu trúc dữ liệu được sử dụng phổ biến nhất theo cú pháp cho các tác vụ mà ngôn ngữ được thiết kế cho.


2

Nói chung tôi thấy thuận tiện để có nghĩa đen cho danh sách, bộ và như vậy. Nhưng đôi khi nó làm tôi bực mình vì tôi không biết gì về việc triển khai thực tế - nói - danh sách Python hoặc mảng Javascript. Điều duy nhất tôi có thể chắc chắn là họ để lộ một giao diện nhất định.

Tôi lấy làm chuẩn mực về khả năng biểu đạt ngôn ngữ, nó có thể viết các cấu trúc dữ liệu của riêng mình như các thư viện như thế nào và sử dụng chúng thuận tiện như thế nào.

Ví dụ, Scala cung cấp các bộ sưu tập khác nhau với các đảm bảo hiệu suất và triển khai khác nhau. Tất cả chúng đều được triển khai trong chính Scala và cú pháp sử dụng chúng chỉ phức tạp hơn một chút so với khi chúng được dựng sẵn và có hỗ trợ thời gian chạy.

Cấu trúc cơ bản duy nhất thực sự cần hỗ trợ từ chính bộ thực thi, ít nhất là trong ngôn ngữ được quản lý, là mảng: nếu bạn không quản lý bộ nhớ, bạn sẽ khó có được một loạt các byte liền kề. Mọi cấu trúc khác có thể được xây dựng từ các mảng và con trỏ (hoặc tham chiếu).


1

APL (và các biến thể hiện đại có liên quan, A +, J và K) có vô hướng, vectơ và ma trận như các cấu trúc dữ liệu hạng nhất.

Vâng, chúng có thể được phản đối như là các biến thể đơn thuần trên mảng. Nhưng họ cũng không phải khai báo phức tạp và không đến từ một thư viện riêng biệt, họ cảm thấy như các cấu trúc dữ liệu phức tạp là một phần hạng nhất của ngôn ngữ.


APL cũng có các mảng lồng nhau và các mảng không phải có kiểu dữ liệu đồng nhất, tất cả tạo nên các cấu trúc dữ liệu rất mạnh.
RFlack

1

Từ quan điểm thiết kế ngôn ngữ, ý kiến ​​của bạn về việc có một cú pháp cụ thể cho cấu trúc dữ liệu trong ngôn ngữ cốt lõi là gì? Đó có phải là một ý tưởng tốt, và mục đích của ngôn ngữ (vv) có thay đổi mức độ tốt của sự lựa chọn này không?

Danh sách và bản đồ bằng chữ và cú pháp đóng tiện lợi là các tính năng cần thiết của ngôn ngữ cấp cao.

Sự khác biệt giữa mã Java này:

Thing t = new Thing();
t.setFoo(3);
t.setBar(6.3);
t.setBaz(true);

và mã Groovy này:

t = new Thing(foo: 3, bar: 6.3, baz: true)

là rất lớn Đó là sự khác biệt giữa chương trình 40.000 dòng và chương trình 10.000 dòng. Vấn đề cú pháp.


Trong C # người ta có thể làm: var t = new Thing(foo: 3, bar: 6.3, baz: true);- chỉ có 4 ký tự nữa.
Công việc

Nó thực sự là cùng một số; mã Groovy nên đọc 'def t = ...'
kevin cline

1

Chắc chắn nó phụ thuộc vào ứng dụng của ngôn ngữ lập trình, nhưng đối với các ngôn ngữ cấp cao hơn, nó sẽ thuận tiện nhất có thể để làm việc với bất kỳ cấu trúc dữ liệu phổ biến nào. Hãy xem danh sách các loại dữ liệu trừu tượng trong Wikipedia để biết ví dụ. Tôi đã tìm thấy các nguyên tắc cơ bản phổ biến nhất sau đây (nhưng tôi cũng muốn nghe ý kiến ​​khác):

  • trình tự theo thứ tự (1 chiều): mảng, hàng đợi, ngăn xếp, danh sách ...
  • sắp xếp các cấu trúc đa chiều : bảng, vector, matrice ..
  • bản đồ : hashmap, dictionary, set, multimap ... (1 chiều)
  • bản đồ đa chiều : chức năng, bản đồ của bản đồ ...
  • các loại biểu đồ : cây, đồ thị có hướng ...

Bạn có thể mô phỏng bất kỳ cấu trúc nào với bất kỳ cấu trúc nào khác - nó chỉ phụ thuộc vào mức độ dễ dàng và rõ ràng của ngôn ngữ lập trình cho phép nó. Ví dụ:

  • hàng đợi và ngăn xếp dễ dàng mô phỏng với các mảng hoặc danh sách, sau này cung cấp các hoạt động như đẩy, bật, dịch chuyển, v.v.
  • trình tự theo thứ tự có thể được mô phỏng với các bản đồ có các phím số
  • các tập hợp có thể được mô phỏng bằng các ánh xạ ánh xạ các giá trị thành boolean
  • hầu hết các loại biểu đồ có thể được mô phỏng bằng các chuỗi hoặc bản đồ lồng nhau
  • các chức năng có thể được sử dụng để mô phỏng bản đồ nếu bạn có thể dễ dàng sửa đổi định nghĩa của chúng

Hầu hết các ngôn ngữ cung cấp ít nhất một loại cho các chuỗi theo thứ tự, một cho bản đồ 1 chiều và một cho bản đồ đa chiều, giới hạn ở các chức năng. Cá nhân, tôi thường bỏ lỡ các bộ và đặt hàng các cấu trúc đa chiều bằng các ngôn ngữ như Perl, PHP, JavaScript, Lua ... vì việc mô phỏng chúng không đủ thuận tiện.


1

Tôi nghĩ rằng đó là một ý tưởng tồi khi có quá nhiều loại dữ liệu đặc quyền nhận được cú pháp đặc biệt. Điều này làm phức tạp cú pháp ngôn ngữ một cách không cần thiết, làm cho mã khó đọc hơn, khiến người mới bắt đầu khó học hơn và làm cho việc phát triển các công cụ cho ngôn ngữ trở nên khó khăn hơn.

Bạn có thể tạo một ngoại lệ cho một số lượng nhỏ các loại cấu trúc dữ liệu rất phổ biến. Tôi có thể cho phép tối đa:

  • Mảng có độ dài cố định
  • Bộ
  • Hashmap
  • Trình tự / danh sách
  • Hồ sơ / cấu trúc / lớp

Bất cứ điều gì phức tạp hơn thế có lẽ nên để các thư viện xử lý, sử dụng cú pháp thông thường của ngôn ngữ cho các loại dữ liệu tùy chỉnh.

Đặc biệt, những thứ như cây Đỏ / Đen, Hàng đợi ưu tiên, v.v ... có khá nhiều tùy chọn triển khai có thể, do đó, không khôn ngoan khi nướng một triển khai cụ thể vào ngôn ngữ cốt lõi. Tốt hơn là để mọi người chọn cách thực hiện phù hợp nhất cho tình huống của họ. Ví dụ về các lựa chọn triển khai mà tôi có thể không muốn một nhà thiết kế ngôn ngữ hạn chế lựa chọn của mình trên:

  • Đột biến hay bất biến?
  • Cho phép null hay không?
  • Đồng bộ hóa hay không?
  • Được hỗ trợ bởi lưu trữ liên tục hay không?
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.