Tôi nghe nhiều ngôn ngữ lập trình mới được gõ động nhưng thực sự nó có nghĩa gì khi chúng ta nói một ngôn ngữ được gõ động so với gõ tĩnh?
Tôi nghe nhiều ngôn ngữ lập trình mới được gõ động nhưng thực sự nó có nghĩa gì khi chúng ta nói một ngôn ngữ được gõ động so với gõ tĩnh?
Câu trả lời:
Một ngôn ngữ được gõ tĩnh nếu loại biến được biết tại thời điểm biên dịch. Đối với một số ngôn ngữ, điều này có nghĩa là bạn là lập trình viên phải chỉ định loại biến của từng biến (ví dụ: Java, C, C ++); các ngôn ngữ khác cung cấp một số dạng suy luận kiểu , khả năng của hệ thống loại để suy ra loại biến (ví dụ: OCaml, Haskell, Scala, Kotlin)
Ưu điểm chính ở đây là tất cả các loại kiểm tra có thể được thực hiện bởi trình biên dịch, và do đó rất nhiều lỗi nhỏ bị bắt gặp ở giai đoạn rất sớm.
Ví dụ: C, C ++, Java, Rust, Go, Scala
Một ngôn ngữ được gõ động nếu loại được liên kết với các giá trị thời gian chạy và không được đặt tên biến / trường / vv. Điều này có nghĩa là bạn là một lập trình viên có thể viết nhanh hơn một chút vì bạn không phải chỉ định các loại mỗi lần (trừ khi sử dụng ngôn ngữ gõ tĩnh với suy luận kiểu ).
Ví dụ: Perl, Ruby, Python, PHP, JavaScript
Hầu hết các ngôn ngữ script đều có tính năng này vì dù sao cũng không có trình biên dịch để kiểm tra kiểu tĩnh, nhưng bạn có thể thấy mình đang tìm kiếm một lỗi do trình thông dịch hiểu sai loại biến. May mắn thay, các kịch bản có xu hướng nhỏ để các lỗi không có nhiều nơi để ẩn.
Hầu hết các ngôn ngữ được gõ động đều cho phép bạn cung cấp thông tin loại, nhưng không yêu cầu. Một ngôn ngữ hiện đang được phát triển, Rascal , sử dụng cách tiếp cận hỗn hợp cho phép nhập động trong các hàm nhưng thực thi gõ tĩnh cho chữ ký hàm.
Các ngôn ngữ lập trình được gõ tĩnh thực hiện kiểm tra kiểu (tức là quá trình xác minh và thực thi các ràng buộc của các loại) tại thời gian biên dịch trái ngược với thời gian chạy .
Các ngôn ngữ lập trình được gõ động thực hiện kiểm tra kiểu vào thời gian chạy trái ngược với thời gian biên dịch .
Ví dụ về các ngôn ngữ được nhập tĩnh là: - Java, C, C ++
Ví dụ về các ngôn ngữ được nhập động là: - Perl, Ruby, Python, PHP, JavaScript
Dưới đây là một ví dụ tương phản với cách Python (gõ động) và Go (gõ tĩnh) xử lý lỗi loại:
def silly(a):
if a > 0:
print 'Hi'
else:
print 5 + '3'
Python thực hiện kiểm tra kiểu trong thời gian chạy và do đó:
silly(2)
Chạy hoàn toàn tốt, và tạo ra đầu ra dự kiến Hi
. Lỗi chỉ được nêu ra nếu dòng có vấn đề bị tấn công:
silly(-1)
Sản xuất
TypeError: unsupported operand type(s) for +: 'int' and 'str'
bởi vì dòng liên quan đã thực sự được thực thi.
Mặt khác, kiểm tra kiểu tại thời gian biên dịch:
package main
import ("fmt"
)
func silly(a int) {
if (a > 0) {
fmt.Println("Hi")
} else {
fmt.Println("3" + 5)
}
}
func main() {
silly(2)
}
Ở trên sẽ không biên dịch, với lỗi sau:
invalid operation: "3" + 5 (mismatched types string and int)
runhaskell
, ví dụ.
Đơn giản chỉ cần đặt nó theo cách này: trong các loại biến ngôn ngữ được gõ tĩnh là tĩnh , nghĩa là một khi bạn đặt biến thành một loại, bạn không thể thay đổi nó. Đó là bởi vì gõ được liên kết với biến chứ không phải giá trị mà nó đề cập đến.
Ví dụ trong Java:
String str = "Hello"; //variable str statically typed as string
str = 5; //would throw an error since str is supposed to be a string only
Mặt khác: trong các loại biến ngôn ngữ được gõ động là động , nghĩa là sau khi bạn đặt biến thành một loại, bạn CÓ THỂ thay đổi nó. Đó là bởi vì gõ được liên kết với giá trị mà nó giả định chứ không phải là chính biến.
Ví dụ trong Python:
str = "Hello" # variable str is linked to a string value
str = 5 # now it is linked to an integer value; perfectly OK
Vì vậy, tốt nhất là nghĩ về các biến trong các ngôn ngữ được gõ động như chỉ là các con trỏ chung cho các giá trị được gõ.
Để tổng hợp, gõ mô tả (hoặc nên mô tả) các biến trong ngôn ngữ chứ không phải chính ngôn ngữ. Nó có thể được sử dụng tốt hơn như một ngôn ngữ với các biến được gõ tĩnh so với ngôn ngữ có các biến được gõ động IMHO.
Các ngôn ngữ được nhập tĩnh thường là các ngôn ngữ được biên dịch, do đó, trình biên dịch kiểm tra các loại (có ý nghĩa hoàn hảo phải không? Vì các loại không được phép thay đổi sau này trong thời gian chạy).
Các ngôn ngữ được gõ động thường được diễn giải, do đó việc kiểm tra kiểu (nếu có) xảy ra vào thời gian chạy khi chúng được sử dụng. Điều này tất nhiên mang lại một số chi phí hiệu năng và là một trong những lý do khiến các ngôn ngữ động (ví dụ: python, ruby, php) không có quy mô tốt như các ngôn ngữ được nhập (java, c #, v.v.). Từ góc độ khác, các ngôn ngữ gõ tĩnh có nhiều chi phí khởi nghiệp: khiến bạn thường viết nhiều mã hơn, mã khó hơn. Nhưng điều đó trả tiền sau.
Điều tốt là cả hai bên đều mượn các tính năng từ phía bên kia. Các ngôn ngữ được nhập đang kết hợp nhiều tính năng động hơn, ví dụ: chung và thư viện động trong c # và các ngôn ngữ động bao gồm kiểm tra loại nhiều hơn, ví dụ: chú thích loại trong python hoặc biến thể HACK của PHP, thường không phải là cốt lõi của ngôn ngữ và có thể sử dụng được trên nhu cầu.
Khi nói đến lựa chọn công nghệ, không bên nào có ưu thế nội tại hơn bên kia. Đây chỉ là vấn đề ưu tiên cho dù bạn muốn kiểm soát nhiều hơn hay bắt đầu linh hoạt. chỉ cần chọn công cụ phù hợp cho công việc và đảm bảo kiểm tra những gì có sẵn trong điều ngược lại trước khi xem xét một công tắc.
http://en.wikipedia.org/wiki/Type_system
Gõ tĩnh
Một ngôn ngữ lập trình được cho là sử dụng kiểu gõ tĩnh khi kiểm tra kiểu được thực hiện trong thời gian biên dịch trái ngược với thời gian chạy. Trong kiểu gõ tĩnh, các kiểu được liên kết với các biến không phải là giá trị. Các ngôn ngữ được nhập tĩnh bao gồm Ada, C, C ++, C #, JADE, Java, Fortran, Haskell, ML, Pascal, Perl (liên quan đến việc phân biệt vô hướng, mảng, băm và chương trình con) và Scala. Gõ tĩnh là một hình thức xác minh chương trình giới hạn (xem loại an toàn): theo đó, nó cho phép nhiều lỗi loại được phát hiện sớm trong chu kỳ phát triển. Trình kiểm tra loại tĩnh chỉ đánh giá thông tin loại có thể được xác định tại thời điểm biên dịch, nhưng có thể xác minh rằng các điều kiện đã kiểm tra giữ cho tất cả các lần thực hiện có thể của chương trình, giúp loại bỏ sự cần thiết phải lặp lại kiểm tra loại mỗi khi chương trình được thực thi. Việc thực hiện chương trình cũng có thể được thực hiện hiệu quả hơn (tức là nhanh hơn hoặc giảm bộ nhớ) bằng cách bỏ qua kiểm tra loại thời gian chạy và cho phép tối ưu hóa khác.
Bởi vì họ đánh giá thông tin loại trong quá trình biên dịch và do đó thiếu thông tin loại chỉ có sẵn trong thời gian chạy, nên trình kiểm tra loại tĩnh là bảo thủ. Họ sẽ từ chối một số chương trình có thể hoạt động tốt trong thời gian chạy, nhưng điều đó không thể được xác định một cách tĩnh để được đánh máy tốt. Ví dụ: ngay cả khi một biểu thức luôn luôn đánh giá là đúng trong thời gian chạy, một chương trình chứa mã
if <complex test> then 42 else <type error>
sẽ bị từ chối khi nhập sai, vì phân tích tĩnh không thể xác định rằng nhánh khác sẽ không được thực hiện. [1] Hành vi bảo thủ của trình kiểm tra loại tĩnh là thuận lợi khi đánh giá sai không thường xuyên: Trình kiểm tra loại tĩnh có thể phát hiện lỗi loại trong các đường dẫn mã hiếm khi được sử dụng. Nếu không kiểm tra kiểu tĩnh, ngay cả các kiểm tra phạm vi mã với độ bao phủ mã 100% có thể không thể tìm thấy các lỗi loại đó. Các thử nghiệm bảo hiểm mã có thể không phát hiện ra các lỗi loại như vậy bởi vì sự kết hợp của tất cả các vị trí nơi các giá trị được tạo và tất cả các vị trí sử dụng một giá trị nhất định phải được tính đến.
Các ngôn ngữ gõ tĩnh được sử dụng rộng rãi nhất không phải là loại chính thức an toàn. Chúng có "lỗ hổng" trong đặc tả ngôn ngữ lập trình cho phép các lập trình viên viết mã phá vỡ xác minh được thực hiện bởi trình kiểm tra kiểu tĩnh và do đó giải quyết một loạt các vấn đề. Ví dụ, Java và hầu hết các ngôn ngữ kiểu C đều có kiểu xảo quyệt và Haskell có các tính năng như unsafePerformIO: các hoạt động đó có thể không an toàn khi chạy, vì chúng có thể gây ra hành vi không mong muốn do nhập sai giá trị khi chương trình chạy.
Gõ động
Một ngôn ngữ lập trình được cho là được gõ động, hoặc chỉ là 'động', khi phần lớn việc kiểm tra kiểu của nó được thực hiện vào thời gian chạy trái ngược với thời gian biên dịch. Trong kiểu gõ động, các kiểu được liên kết với các giá trị không phải là biến. Các ngôn ngữ được nhập động bao gồm Groovy, JavaScript, Lisp, Lua, Objective-C, Perl (đối với các loại do người dùng xác định nhưng không phải là các loại tích hợp), PHP, Prolog, Python, Ruby, Smalltalk và Tcl. So với gõ tĩnh, gõ động có thể linh hoạt hơn (ví dụ: bằng cách cho phép các chương trình tạo các loại và chức năng dựa trên dữ liệu thời gian chạy), mặc dù chi phí ít hơn một đảm bảo tiên nghiệm. Điều này là do một ngôn ngữ gõ động chấp nhận và cố gắng thực thi một số chương trình có thể bị quy định là không hợp lệ bởi trình kiểm tra loại tĩnh.
Gõ động có thể dẫn đến lỗi loại thời gian chạy, đó là, trong thời gian chạy, một giá trị có thể có loại không mong muốn và một thao tác vô nghĩa đối với loại đó được áp dụng. Hoạt động này có thể xảy ra rất lâu sau khi xảy ra lỗi lập trình, đó là nơi mà loại dữ liệu sai được truyền vào một nơi không nên có. Điều này làm cho lỗi khó xác định vị trí.
Các hệ thống ngôn ngữ được gõ động, so với các anh em họ gõ tĩnh của chúng, thực hiện kiểm tra "thời gian biên dịch" ít hơn trên mã nguồn (ví dụ, sẽ kiểm tra xem chương trình có đúng về mặt cú pháp không). Kiểm tra thời gian chạy có thể có khả năng phức tạp hơn, vì chúng có thể sử dụng thông tin động cũng như bất kỳ thông tin nào có trong quá trình biên dịch. Mặt khác, kiểm tra thời gian chạy chỉ xác nhận rằng các điều kiện giữ trong một thực thi cụ thể của chương trình và các kiểm tra này được lặp lại cho mỗi lần thực hiện chương trình.
Phát triển trong các ngôn ngữ gõ động thường được hỗ trợ bởi các thực tiễn lập trình như kiểm tra đơn vị. Kiểm thử là một thông lệ quan trọng trong phát triển phần mềm chuyên nghiệp và đặc biệt quan trọng trong các ngôn ngữ được gõ động. Trong thực tế, kiểm tra được thực hiện để đảm bảo hoạt động chương trình chính xác có thể phát hiện phạm vi lỗi rộng hơn nhiều so với kiểm tra kiểu tĩnh, nhưng ngược lại không thể tìm kiếm toàn diện các lỗi mà cả kiểm tra và kiểm tra loại tĩnh đều có thể phát hiện. Kiểm thử có thể được tích hợp vào chu trình xây dựng phần mềm, trong trường hợp đó có thể được coi là kiểm tra "thời gian biên dịch", trong đó người dùng chương trình sẽ không phải chạy thủ công các kiểm tra đó.
Người giới thiệu
- Xỏ, Benjamin (2002). Các loại và ngôn ngữ lập trình. Báo chí MIT. SỐ 0-262-16209-1.
myObject[remoteDataName]
. Sau đó, không có cách nào để biết nó sẽ chọn tài sản nào hoặc thậm chí nếu đó là một tài sản hợp lệ.
Thuật ngữ "gõ động" không may bị sai. Tất cả các ngôn ngữ được gõ tĩnh và các loại là thuộc tính của biểu thức (không phải là giá trị như một số người nghĩ). Tuy nhiên, một số ngôn ngữ chỉ có một loại. Chúng được gọi là ngôn ngữ uni-gõ. Một ví dụ về ngôn ngữ như vậy là phép tính lambda chưa được kiểm tra.
Trong phép tính lambda chưa được đánh dấu, tất cả các thuật ngữ là thuật ngữ lambda và thao tác duy nhất có thể được thực hiện theo một thuật ngữ là áp dụng nó cho một thuật ngữ khác. Do đó, tất cả các hoạt động luôn dẫn đến đệ quy vô hạn hoặc thuật ngữ lambda, nhưng không bao giờ báo hiệu lỗi.
Tuy nhiên, nếu chúng ta tăng số phép tính lambda chưa được xử lý bằng số nguyên thủy và phép toán số học, thì chúng ta có thể thực hiện các phép toán vô nghĩa, như vậy thêm hai thuật ngữ lambda lại với nhau : (λx.x) + (λy.y)
. Người ta có thể lập luận rằng điều duy nhất cần làm là báo hiệu lỗi khi điều này xảy ra, nhưng để có thể làm điều này, mỗi giá trị phải được gắn thẻ với một chỉ báo cho biết thuật ngữ đó là thuật ngữ lambda hay số. Sau đó, toán tử bổ sung sẽ kiểm tra xem thực sự cả hai đối số được gắn thẻ là số và nếu chúng không, báo hiệu lỗi. Lưu ý rằng các thẻ này không phải là loại, vì loại là thuộc tính của chương trình, không phải giá trị được tạo bởi các chương trình đó.
Một ngôn ngữ chưa được gõ mà thực hiện điều này được gọi là gõ động.
Các ngôn ngữ như JavaScript, Python và Ruby đều không được gõ. Một lần nữa, typeof
toán tử trong JavaScript và type
hàm trong Python có các tên sai; chúng trả về các thẻ được liên kết với toán hạng, không phải kiểu của chúng. Tương tự, dynamic_cast
trong C ++ và instanceof
trong Java không thực hiện kiểm tra kiểu.
"Khi mã nguồn được dịch"
"Khi các loại được kiểm tra"
5 + '3'
là một ví dụ về lỗi loại trong các ngôn ngữ được gõ mạnh như Go và Python, vì chúng không cho phép "ép buộc kiểu" -> khả năng giá trị thay đổi loại trong các ngữ cảnh nhất định như hợp nhất hai loại. Các ngôn ngữ được gõ yếu , chẳng hạn như JavaScript, sẽ không gây ra lỗi loại (kết quả là '53'
).
Các định nghĩa của "Tĩnh & Biên dịch" và "Động & Giải thích" khá giống nhau ... nhưng hãy nhớ đó là "khi các loại được kiểm tra" so với "khi mã nguồn được dịch".
Bạn sẽ nhận được các lỗi cùng loại bất kể ngôn ngữ được biên dịch hay giải thích ! Bạn cần tách các thuật ngữ này về mặt khái niệm.
Năng động, diễn giải
def silly(a):
if a > 0:
print 'Hi'
else:
print 5 + '3'
silly(2)
Vì Python được giải thích và gõ động, nên nó chỉ dịch và kiểm tra loại mã mà nó đang thực thi. Các else
khối không bao giờ thực hiện, vì vậy 5 + '3'
thậm chí không bao giờ nhìn vào!
Điều gì nếu nó được gõ tĩnh?
Một lỗi loại sẽ được ném trước khi mã thậm chí chạy. Nó vẫn thực hiện kiểm tra kiểu trước thời gian chạy mặc dù nó được diễn giải.
Nếu nó được biên soạn thì sao?
Các else
khối sẽ được dịch / nhìn trước khi thời gian chạy, nhưng bởi vì nó tự động gõ nó sẽ không ném ra một lỗi! Các ngôn ngữ được nhập động không kiểm tra các loại cho đến khi thực thi và dòng đó không bao giờ thực thi.
Tĩnh, Biên dịch
package main
import ("fmt"
)
func silly(a int) {
if (a > 0) {
fmt.Println("Hi")
} else {
fmt.Println("3" + 5)
}
}
func main() {
silly(2)
}
Các loại được kiểm tra trước khi chạy (tĩnh) và lỗi loại ngay lập tức bị bắt! Các loại vẫn sẽ được kiểm tra trước thời gian chạy nếu nó được diễn giải, có cùng kết quả. Nếu nó là động, nó sẽ không đưa ra bất kỳ lỗi nào mặc dù mã sẽ được xem xét trong quá trình biên dịch.
Một ngôn ngữ được biên dịch sẽ có hiệu suất tốt hơn trong thời gian chạy nếu nó được nhập tĩnh (so với động); kiến thức về các loại cho phép tối ưu hóa mã máy.
Các ngôn ngữ được nhập tĩnh có hiệu suất tốt hơn vào thời gian chạy về bản chất do không cần phải kiểm tra các loại động trong khi thực thi (nó kiểm tra trước khi chạy).
Tương tự, các ngôn ngữ được biên dịch sẽ nhanh hơn trong thời gian chạy vì mã đã được dịch thay vì cần phải "diễn giải" / dịch nó một cách nhanh chóng.
Lưu ý rằng cả hai ngôn ngữ được biên dịch và gõ tĩnh sẽ có độ trễ trước khi chạy để dịch và kiểm tra kiểu, tương ứng.
Gõ tĩnh bắt lỗi sớm, thay vì tìm thấy chúng trong khi thực hiện (đặc biệt hữu ích cho các chương trình dài). Điều "nghiêm ngặt" hơn ở chỗ nó sẽ không cho phép lỗi loại ở bất cứ đâu trong chương trình của bạn và thường ngăn các biến thay đổi loại, điều này sẽ bảo vệ chống lại các lỗi không mong muốn.
num = 2
num = '3' // ERROR
Gõ động là linh hoạt hơn, mà một số đánh giá cao. Nó thường cho phép các biến thay đổi các loại, điều này có thể dẫn đến các lỗi không mong muốn.
Các ngôn ngữ được nhập tĩnh : từng biến và biểu thức đã được biết đến tại thời điểm biên dịch.
( int a;
a chỉ có thể lấy các giá trị kiểu số nguyên khi chạy)
Ví dụ: C, C ++, Java
Các ngôn ngữ được nhập động : các biến có thể nhận các giá trị khác nhau trong thời gian chạy và loại của chúng được xác định khi chạy.
( var a;
a có thể lấy bất kỳ loại giá trị nào trong thời gian chạy)
Ví dụ: Ruby, Python.
Kiểm tra loại ngôn ngữ gõ tĩnh tại thời gian biên dịch và loại KHÔNG thể thay đổi. (Đừng dễ thương với các nhận xét kiểu, một biến / tham chiếu mới được tạo).
Kiểm tra loại ngôn ngữ được gõ động vào thời gian chạy và loại biến có thể được thay đổi vào thời gian chạy.
Các định nghĩa đơn giản và ngọt ngào, nhưng phù hợp với nhu cầu: Các ngôn ngữ được nhập tĩnh liên kết loại với một biến cho toàn bộ phạm vi của nó (Seg: SCALA) Các ngôn ngữ được gõ động liên kết loại với giá trị thực được tham chiếu bởi một biến.
Các ngôn ngữ được nhập tĩnh như C ++, Java và các ngôn ngữ được nhập động như Python chỉ khác nhau về cách thực hiện loại biến. Các ngôn ngữ được nhập tĩnh có kiểu dữ liệu tĩnh cho biến, ở đây, kiểu dữ liệu được kiểm tra trong quá trình biên dịch nên việc gỡ lỗi đơn giản hơn nhiều ... trong khi các ngôn ngữ được gõ động không làm như vậy, kiểu dữ liệu được kiểm tra để thực thi chương trình và do đó gỡ lỗi là một chút khó khăn.
Hơn nữa, chúng có một sự khác biệt rất nhỏ và có thể liên quan đến các ngôn ngữ được gõ mạnh và gõ yếu . Một ngôn ngữ được gõ mạnh không cho phép bạn sử dụng một loại như một ngôn ngữ khác, vd. C và C ++ ... trong khi các ngôn ngữ được gõ yếu cho phép eg.python
Gõ tĩnh
Các loại được kiểm tra trước thời gian chạy để có thể bắt lỗi sớm hơn.
Ví dụ = c ++
Tự động gõ
Các loại được kiểm tra trong khi thực hiện.
Ví dụ = Python
Các ngôn ngữ gõ tĩnh (trình biên dịch giải quyết các cuộc gọi phương thức và biên dịch các tham chiếu):
Ngôn ngữ gõ động (quyết định đưa ra trong chương trình đang chạy):
ngôn ngữ gõ động giúp nhanh chóng tạo ra các khái niệm thuật toán nguyên mẫu mà không cần phải suy nghĩ về việc sử dụng loại biến nào (đây là điều cần thiết trong ngôn ngữ được gõ tĩnh e).
Nhập tĩnh: Các ngôn ngữ như Java và Scala được gõ tĩnh.
Các biến phải được xác định và khởi tạo trước khi chúng được sử dụng trong mã.
cho người cũ int x; x = 10;
System.out.println (x);
Gõ động: Perl là một ngôn ngữ gõ động.
Các biến không cần phải được khởi tạo trước khi chúng được sử dụng trong mã.
y = 10; sử dụng biến này trong phần sau của mã
$
), mảng ( @
) và hàm băm ( %
). Loại biến trong Perl được biết đến tại thời gian biên dịch và giữ nguyên cho phần còn lại của vòng đời biến.