Các loại cụ thể vẫn cần thiết?


20

Một điều xảy ra với tôi vào một ngày khác, là các loại cụ thể vẫn cần thiết hoặc một di sản đang giữ chúng tôi lại. Ý tôi là: chúng ta có thực sự cần short, int, long, bigint, v.v.

Tôi hiểu lý do, các biến / đối tượng được giữ trong bộ nhớ, bộ nhớ cần được phân bổ và do đó chúng ta cần biết một biến có thể lớn đến mức nào. Nhưng thực sự, không phải một ngôn ngữ lập trình hiện đại có thể xử lý "các loại thích ứng", nghĩa là, nếu một thứ gì đó chỉ được phân bổ trong phạm vi ngắn thì nó sử dụng ít byte hơn và nếu một thứ gì đó được cấp phát một số lượng rất lớn thì bộ nhớ được cấp phát accordinly cho trường hợp cụ thể đó.

Float, real và double là một chút phức tạp hơn vì loại phụ thuộc vào độ chính xác bạn cần. Tuy nhiên, các chuỗi có thể chiếm ít bộ nhớ hơn trong nhiều trường hợp (trong .Net) trong đó phần lớn ascii được sử dụng nhưng các chuỗi luôn chiếm gấp đôi bộ nhớ do mã hóa unicode.

Một đối số cho các loại cụ thể có thể là đó là một phần của đặc tả, ví dụ: một biến không thể lớn hơn một giá trị nhất định nên chúng tôi đặt nó thành ngắn. Nhưng tại sao không có ràng buộc loại thay thế? Sẽ linh hoạt và mạnh mẽ hơn nhiều khi có thể đặt các phạm vi và giá trị cho phép trên các biến (và thuộc tính).

Tôi nhận ra vấn đề to lớn trong việc cải tiến kiến ​​trúc kiểu vì nó được tích hợp chặt chẽ với phần cứng cơ bản và những thứ như tuần tự hóa thực sự có thể trở nên khó khăn. Nhưng từ góc độ lập trình thì có nên tuyệt vời không?


6
PHP, Ruby, Perl và những người khác không yêu cầu bạn nêu các loại biến. Môi trường chỉ ra nó cho bạn.
Thất vọngWithFormsDesigner

7
Các chuỗi Unicode không phải chiếm thêm bộ nhớ khi chúng chỉ được sử dụng cho ASCII (UTF-8).

2
Nhưng có một sự khác biệt giữa các loại biến thể và thích nghi IMO. Các biến thể hoàn toàn không được gõ nhưng được gõ khi được gán, trong khi các kiểu thích ứng sẽ được gõ, nhưng lỏng lẻo hơn. (và tôi thích khái niệm về các ràng buộc kiểu)
Homde

Điều này làm tôi nhớ đến dự án này: tom.lokhorst.eu/media/iêu
LennyProgrammer

4
Ada thì sao? type hour is range 0 .. 23;
mouviciel

Câu trả lời:


12

Tôi hoàn toàn tin rằng đây là trường hợp. Các ràng buộc ngữ nghĩa có giá trị nhiều hơn các ràng buộc thực hiện. Lo lắng về kích thước của một cái gì đó cảm thấy như lo lắng về tốc độ của một cái gì đó khi lập trình hướng đối tượng đang đến.

Nó đã không thay thế hiệu suất lập trình quan trọng. Nó chỉ làm cho chương trình quan trọng không hiệu suất trở nên hiệu quả hơn.


1
Kiểm tra Hợp đồng mã trong .NET 4.0.
Steven Jeuris

+1 Khi nói đến lưu trữ / truyền dữ liệu (ví dụ: mạng), các ràng buộc là cơ bản để tối đa hóa hiệu quả của giao thức / thực hiện. Ngoài ra, có rất nhiều nền tảng để đạt được nếu bộ sưu tập đánh máy có sẵn. Ngoài ra, thật an toàn khi cho rằng hiệu quả có thể bị lạc hậu (đặc biệt là nếu nó làm giảm khả năng xảy ra lỗi ngữ nghĩa).
Planice

9

Các loại thích ứng có nghĩa là logic để thực hiện việc thích ứng, có nghĩa là làm việc trong thời gian chạy để chạy logic đó (tạo khuôn mẫu và thời gian biên dịch sẽ yêu cầu một loại cụ thể, suy luận kiểu là trường hợp đặc biệt trong đó bạn có được hai thế giới tốt nhất). Công việc làm thêm đó có thể ổn trong môi trường mà việc biểu diễn không quan trọng và hệ thống giữ kích thước hợp lý. Trong các môi trường khác thì không (các hệ thống nhúng là một, trong đó đôi khi bạn phải sử dụng các loại số nguyên 32/64 bit cho hiệu suất cpu và các loại số nguyên 8/16 bit để tối ưu hóa sao lưu bộ nhớ tĩnh).

Ngay cả các ngôn ngữ có mục đích chung hỗ trợ liên kết muộn (độ phân giải của các loại trong thời gian chạy, như VB6) có xu hướng thúc đẩy gõ mạnh ngay bây giờ (VB.NET), do cú đánh hiệu suất được sử dụng phát sinh khi liên kết muộn bị lạm dụng và vì bạn thường xuyên bị lạm dụng kết thúc với mã xấu khi các loại không rõ ràng ( Tham khảo / Tái cấu trúc chuyên nghiệp trong Visual Basic - Danijel Arsenovski ).


Vui lòng xác định "tự động gõ".

@delnan: thay thế tự động gõ bằng liên kết muộn là ý tôi muốn nói :)
Matthieu

Có rất nhiều ngôn ngữ có mục đích chung giải quyết các loại trong thời gian chạy, Common Lisp chỉ đặt tên một ngôn ngữ. (Đối với mục đích hiệu suất, bạn có thể khai báo các loại trong Common Lisp, do đó bạn chỉ có thể làm như vậy trong các phần quan trọng về hiệu suất.)
David Thornley 2/211

@David Thornley: "thực thi" gõ mạnh có thể quá mạnh, "quảng bá" sẽ phù hợp hơn, cập nhật câu trả lời của tôi cho phù hợp. Một ngôn ngữ cho phép bạn chọn giữa hai loại ràng buộc tùy thuộc vào tình huống chắc chắn tốt hơn là bị ép buộc theo cách này hay cách khác. Đặc biệt là khi không làm lập trình cấp thấp, và tập trung vào logic.
Matthieu

4

Đơn giản, bộ nhớ và tốc độ Khi tôi khai báo một biến, bộ nhớ cho biến đó được phân bổ trong một khối. Để hỗ trợ một biến tăng trưởng động, tôi sẽ phải thêm khái niệm bộ nhớ không liền kề vào biến đó (hoặc là dự trữ khối lớn nhất mà biến có thể biểu thị). Bộ nhớ không liền kề sẽ làm giảm hiệu suất khi gán / truy xuất. Phân bổ lớn nhất có thể sẽ là lãng phí trong kịch bản mà tôi chỉ cần một byte nhưng hệ thống dự trữ lâu dài.

Hãy nghĩ về sự đánh đổi giữa một mảng và một vectơ (hoặc danh sách được liên kết). Với một mảng, tìm kiếm một vị trí cụ thể là một vấn đề đơn giản để có được vị trí bắt đầu và dịch chuyển con trỏ bộ nhớ x khoảng trắng để xác định vị trí mới đó trong bộ nhớ. Hãy nghĩ về một int như một bit [32] đọc một int liên quan đến việc đi qua mảng đó để có được tất cả các giá trị bit.

Để tạo một kiểu số động, bạn phải thay đổi từ một mảng bit thành một vectơ bit. Đọc số động của bạn liên quan đến việc đi đến đầu, nhận bit đó, hỏi vị trí của bit tiếp theo trong bộ nhớ, di chuyển đến vị trí đó, nhận bit đó, v.v. Với mỗi bit trong số động, bạn đang thực hiện ba thao tác đọc ( hiện tại), đọc (địa chỉ tiếp theo), di chuyển (tiếp theo). Hãy tưởng tượng đọc các giá trị của một triệu số. Đó là một triệu hoạt động thêm. Nó có vẻ không đáng kể. Nhưng hãy nghĩ về các hệ thống (như tài chính), nơi mỗi mili giây quan trọng.

Quyết định đã được đưa ra rằng việc đưa onus cho nhà phát triển để kiểm tra kích thước và xác nhận là một sự đánh đổi nhỏ so với ảnh hưởng đến hiệu suất của hệ thống.


1
Cách khác là triển khai các số tương tự như danh sách mảng trong đó mảng được phân bổ lại khi số đó vượt quá kích thước hiện tại. Ngoài ra, bạn phải tính đến trường hợp người dùng MUỐN kết nối vòng lặp tràn.
Michael Brown

Điều đó đúng, nhưng phần nào đơn giản hóa. Bạn có thể đưa ra một cấu trúc mảng hiệu quả hơn, trong khi không nhanh như gõ tĩnh có thể "đủ nhanh" trong hầu hết các trường hợp. chẳng hạn, bạn có thể lưu thông tin trên các khối thuộc các loại khác nhau, nếu mảng không bị lởm chởm hoàn toàn sẽ không chiếm nhiều bộ nhớ hoặc hiệu năng hơn. Hoặc mảng có thể hy sinh một số bộ nhớ để có một chỉ mục của một số loại. Mảng thậm chí có thể tự tối ưu hóa dựa trên nội dung của nó. Bạn vẫn có thể có tùy chọn nhập kích thước bộ nhớ thông qua một ràng buộc kiểu nếu bạn cần hiệu suất.
Homde

Công bằng mà nói, nó không tàn bạo như bạn nghĩ ra. Cf câu trả lời sắp tới của tôi.
Paul Nathan

3

Các loại cụ thể được yêu cầu cho các ngôn ngữ và dự án tập trung vào phần cứng. Một ví dụ là các giao thức mạng trực tuyến.

Nhưng hãy tạo - cho vui - một loại varint trong một ngôn ngữ như C ++. Xây dựng nó từ một newmảng ints.

Không khó để thực hiện bổ sung: chỉ xor các byte với nhau và kiểm tra các bit cao: nếu có một phép toán mang, newtrong một byte trên mới và mang bit qua. Phép trừ theo sau tầm thường trong biểu diễn bổ sung của 2. (Điều này còn được gọi là một adder mang gợn).

Phép nhân theo sau tương tự; sử dụng lặp / thêm. Như mọi khi, xoắn thực sự trong đuôi của bạn là phân chia [*].

Bạn đã mất gì khi điều này xảy ra, mặc dù?

  • Thời gian quyết định. Bạn có một tòa nhà chọc trời ( new) có thể kích hoạt tại các điểm không nhất thiết phải kiểm soát được.

  • Không gian quyết đoán.

  • Toán bán phần mềm chậm.

Nếu bạn cần sử dụng ngôn ngữ lớp phần cứng và cũng cần phải hoạt động ở mức cao (chậm) và không muốn nhúng công cụ viết kịch bản, điều này varintrất có ý nghĩa. Có lẽ nó được viết ở đâu đó.

[*] Thuật toán toán phần cứng Cf cho các cách thực hiện nhanh hơn - thường thì thủ thuật là các hoạt động song song.


2

Đây là một câu hỏi hay. Nó giải thích tại sao một ngôn ngữ như Python không cần "short, int, long, bigint, v.v.": số nguyên là, tốt, số nguyên (có một loại số nguyên duy nhất trong Python 3) và không có kích thước giới hạn (vượt quá giới hạn bộ nhớ của máy tính, tất nhiên).

Đối với Unicode, mã hóa UTF-8 (là một phần của Unicode) chỉ sử dụng một ký tự duy nhất cho các ký tự ASCII, vì vậy nó không tệ.

Tổng quát hơn, các ngôn ngữ động dường như đi theo hướng mà bạn đề cập. Tuy nhiên, vì lý do hiệu quả, các loại ràng buộc hơn rất hữu ích trong một số trường hợp (như các chương trình phải chạy nhanh). Tôi không thấy nhiều thay đổi trong tương lai gần, vì các bộ xử lý tổ chức dữ liệu theo byte (hoặc 2, 4, 8, v.v. byte).


1

Trên cơ sở lý thuyết ngôn ngữ, bạn đã đúng. Các loại nên dựa trên một tập hợp các trạng thái pháp lý, các phép biến đổi có sẵn cho các trạng thái đó và các hoạt động có thể thực hiện được trên các trạng thái đó.

Tuy nhiên, đây gần như là những gì lập trình OOP ở dạng điển hình mang lại cho bạn. Trong thực tế, trong Java, bạn đang có hiệu quả nói về BigIntegerBigDecimallớp học, trong đó phân bổ không gian dựa trên bao nhiêu là cần thiết để lưu trữ các đối tượng. (Như FrustratedWithFormsDesigner đã lưu ý, nhiều ngôn ngữ loại kịch bản thậm chí còn đi xa hơn trên đường dẫn này và thậm chí không yêu cầu khai báo loại và sẽ lưu trữ bất cứ thứ gì bạn cung cấp cho chúng.)

Tuy nhiên, hiệu suất vẫn có liên quan và vì tốn kém khi chuyển đổi các loại trong thời gian chạy và vì trình biên dịch không thể đảm bảo kích thước tối đa của biến trong thời gian biên dịch, chúng tôi vẫn có các biến có kích thước tĩnh cho các loại đơn giản trong nhiều ngôn ngữ.


Tôi nhận ra rằng một số kiểu gõ động / thích ứng có vẻ tốn kém và ít hiệu năng hơn so với những gì chúng ta có bây giờ, và sử dụng các trình biên dịch hiện tại chắc chắn sẽ có. Nhưng chúng tôi có chắc chắn 100% rằng nếu bạn xây dựng một ngôn ngữ và trình biên dịch từ đầu, bạn không thể tạo ra chúng, nếu không nhanh như gõ tĩnh, thì ít nhất có thể nhanh chóng có giá trị.
Homde

1
@MKO: Tại sao bạn không thử và xem?
Anon.

1
Có, bạn có thể làm cho nó nhanh một cách khả thi (nhưng có lẽ không bao giờ nhanh như một hệ thống tĩnh cho các số). Nhưng phần "có đáng không" thì khó hơn. Hầu hết mọi người làm việc với dữ liệu có phạm vi phù hợp thoải mái trong một inthoặc một double, và nếu không, họ biết về nó, vì vậy kích thước giá trị động là một tính năng mà họ không cần phải trả tiền.
jprete

Như tất cả các lập trình viên tất nhiên tôi mơ ước một ngày nào đó sẽ tạo ra ngôn ngữ của riêng tôi;)
Homde

@jprete: Tôi không đồng ý; hầu hết mọi người không biết về kết quả trung gian lớn có thể. Một ngôn ngữ như vậy có thể và đã được thực hiện đủ nhanh cho hầu hết các mục đích.
David Thornley

1

Nó phụ thuộc vào ngôn ngữ. Đối với các ngôn ngữ cấp cao hơn như Python, Ruby, Erlang và như vậy bạn chỉ có khái niệm về số nguyên và số thập phân.

Tuy nhiên, đối với một loại ngôn ngữ nhất định có các loại này là rất quan trọng. Khi bạn đang viết mã để đọc và viết các định dạng nhị phân như PNG, JPeg, v.v., bạn cần biết chính xác có bao nhiêu thông tin đang được đọc tại một thời điểm. Tương tự với việc viết kernel hệ điều hành và trình điều khiển thiết bị. Không phải ai cũng làm điều này, và trong các ngôn ngữ cấp cao hơn, họ sử dụng các thư viện C để thực hiện các công việc nặng nề chi tiết.

Trong shortđó, vẫn còn một nơi dành cho các loại cụ thể hơn, nhưng nhiều vấn đề phát triển không đòi hỏi độ chính xác đó.


0

Gần đây tôi đã tạo một trình soạn thảo logic bậc thang và thời gian chạy và tôi quyết định rất hạn chế với các loại:

  • Boolean
  • Con số
  • Chuỗi
  • Ngày giờ

Tôi tin rằng nó làm cho nó trực quan hơn cho người dùng. Đây là một sự khởi đầu triệt để từ hầu hết các PLC có tất cả các loại "bình thường" mà bạn thấy trong một ngôn ngữ như C.


0

Ngôn ngữ lập trình đã được di chuyển theo hướng đó. Lấy chuỗi chẳng hạn. Trong các ngôn ngữ cũ, bạn phải khai báo kích thước của chuỗi, như PIC X(42)trong COBOL, DIM A$(42)trong một số phiên bản BASIC hoặc [ VAR] CHAR(42)trong SQL. Trong các ngôn ngữ hiện đại, bạn chỉ có một stringloại được phân bổ động và không cần phải suy nghĩ về kích thước.

Số nguyên là khác nhau, tuy nhiên:

Ý tôi là: chúng ta có thực sự cần short, int, long, bigint, v.v.

Hãy nhìn vào Python. Nó được sử dụng để phân biệt giữa các số nguyên có kích thước máy ( int) và kích thước tùy ý ( long). Trong 3.x cái trước đã biến mất (cái cũ longlà cái mới int) và không ai bỏ lỡ nó.

Nhưng vẫn còn một loại chuyên biệt cho chuỗi số nguyên 8 bit ở dạng bytesbytearray. Tại sao không sử dụng một tuplehoặc listsố nguyên tương ứng? Đúng, bytescó thêm các phương thức giống như chuỗi tuplekhông, nhưng chắc chắn hiệu quả có rất nhiều để làm với nó.

Float, real và double là một chút phức tạp hơn vì loại phụ thuộc vào độ chính xác bạn cần.

Không hẳn vậy. Cách tiếp cận "mọi thứ đều chính xác kép" là rất phổ biến.


1
Có lẽ các loại cơ sở nên khai báo mục đích cơ bản của loại, tức là int cho các số "thông thường", gấp đôi cho tất cả các "số thập phân" thông thường (không nên int có thể có số thập phân mặc dù đơn giản?) "Tiền" để làm việc với số lượng và byte để làm việc với dữ liệu nhị phân. Một ràng buộc kiểu được khai báo thông qua một thuộc tính có thể cho phép khai báo phạm vi được phép, độ chính xác thập phân, không có giá trị và thậm chí các giá trị được phép. Sẽ thật tuyệt nếu bạn có thể tạo các loại tùy chỉnh và có thể tái sử dụng theo cách đó
Homde

@konrad: IMHO, lý do số nguyên "không dấu" gây ra sự đau đầu như vậy trong C là đôi khi chúng được sử dụng để biểu diễn các số và đôi khi được sử dụng để đại diện cho các thành viên của vòng đại số trừu tượng. Việc có các loại "vòng" và "số không dấu" riêng biệt có thể đảm bảo rằng mã like unum64 += ring32a-ring32bsẽ luôn mang lại hành vi chính xác, bất kể loại số nguyên mặc định là 16 bit hay 64 [lưu ý rằng việc sử dụng +=là cần thiết; một biểu thức như thế unum64a = unum64b + (ring32a-ring32b);nên bị từ chối là mơ hồ.]
supercat

0

Tôi hiểu lý do, các biến / đối tượng được giữ trong bộ nhớ, bộ nhớ cần được phân bổ và do đó chúng ta cần biết một biến có thể lớn đến mức nào. Nhưng thực sự, không phải một ngôn ngữ lập trình hiện đại có thể xử lý "các loại thích ứng", nghĩa là, nếu một thứ gì đó chỉ được phân bổ trong phạm vi ngắn thì nó sử dụng ít byte hơn và nếu một thứ gì đó được cấp phát một số lượng rất lớn thì bộ nhớ được cấp phát accordinly cho trường hợp cụ thể đó.

Float, real và double là một chút phức tạp hơn vì loại phụ thuộc vào độ chính xác bạn cần. Tuy nhiên, các chuỗi có thể chiếm ít bộ nhớ hơn trong nhiều trường hợp (trong .Net) trong đó phần lớn ascii được sử dụng nhưng các chuỗi luôn chiếm gấp đôi bộ nhớ do mã hóa unicode.

Fortran đã có một cái gì đó tương tự (tôi không biết chính xác đây có phải là ý của bạn không, vì tôi thực sự thấy hai câu hỏi). Ví dụ, trong F90 trở lên, bạn không cần xác định rõ ràng kích thước loại , có thể nói. Điều này là tốt, không chỉ vì nó cung cấp cho bạn một vị trí trung tâm để xác định các loại dữ liệu của bạn, mà còn là một cách di động để xác định chúng. REAL * 4 không giống nhau trong tất cả các triển khai trên tất cả các bộ xử lý (và theo bộ xử lý, ý tôi là CPU + trình biên dịch), không phải bởi một hình ảnh dài.

chọn_real_kind (p, r) trả về giá trị loại của loại dữ liệu thực với độ chính xác thập phân lớn hơn ít nhất là p chữ số và phạm vi số mũ lớn hơn ít nhất r.

Vì vậy, bạn đi, ví dụ;

program real_kinds
integer,parameter :: p6 = selected_real_kind(6)
integer,parameter :: p10r100 = selected_real_kind(10,100) !p is precision, r is range
integer,parameter :: r400 = selected_real_kind(r=400)
real(kind=p6) :: x
real(kind=p10r100) :: y
real(kind=r400) :: z

print *, precision(x), range(x)
print *, precision(y), range(y)
print *, precision(z), range(z)
end program real_kinds

(Tôi nghĩ đó là một ví dụ khá tự giải thích).

Vẫn không biết nếu tôi hiểu chính xác câu hỏi của bạn, và đây là những gì bạn nghĩ.

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.