Dużo słyszę, że nowe języki programowania są dynamicznie wpisywane, ale co to właściwie oznacza, gdy mówimy, że język jest dynamicznie pisany a nie statyczny?
Dużo słyszę, że nowe języki programowania są dynamicznie wpisywane, ale co to właściwie oznacza, gdy mówimy, że język jest dynamicznie pisany a nie statyczny?
Odpowiedzi:
Język jest wpisywany statycznie, jeśli typ zmiennej jest znany w czasie kompilacji. W przypadku niektórych języków oznacza to, że jako programista musisz określić, jakiego typu jest każda zmienna (np .: Java, C, C ++); inne języki oferują pewną formę wnioskowania o typie , zdolność systemu typów do wywnioskowania typu zmiennej (np .: OCaml, Haskell, Scala, Kotlin)
Główną zaletą jest to, że kompilator może wykonywać wszelkiego rodzaju sprawdzanie, a zatem wiele trywialnych błędów jest wykrywanych na bardzo wczesnym etapie.
Przykłady: C, C ++, Java, Rust, Go, Scala
Język jest dynamicznie wpisywany, jeśli ten typ jest powiązany z wartościami w czasie wykonywania, a nie nazwanymi zmiennymi / polami / itp. Oznacza to, że jako programista możesz pisać trochę szybciej, ponieważ nie musisz za każdym razem określać typów (chyba że używasz języka o typie statycznym z wnioskowaniem typu ).
Przykłady: Perl, Ruby, Python, PHP, JavaScript
Większość języków skryptowych ma tę funkcję, ponieważ i tak nie ma kompilatora do statycznego sprawdzania typu, ale może się zdarzyć, że szukasz błędu wynikającego z błędnej interpretacji typu zmiennej przez interpreter. Na szczęście skrypty są zwykle małe, więc błędy nie mają tak wielu miejsc do ukrycia.
Większość języków z dynamicznym pisaniem pozwala na podanie informacji o typie, ale nie wymaga ich. Jeden z rozwijanych obecnie języków, Rascal , stosuje podejście hybrydowe, umożliwiając dynamiczne pisanie w obrębie funkcji, ale wymuszając pisanie statyczne dla podpisu funkcji.
Statycznie typowane języki programowania sprawdzają typy (tj. Proces weryfikacji i egzekwowania ograniczeń typów) w czasie kompilacji, w przeciwieństwie do czasu wykonywania .
Dynamicznie wpisywane języki programowania sprawdzają typ w czasie wykonywania, w przeciwieństwie do czasu kompilacji .
Przykłady języków o typie statycznym to: - Java, C, C ++
Przykłady dynamicznie wpisywanych języków to: - Perl, Ruby, Python, PHP, JavaScript
Oto przykład kontrastujący z tym, jak Python (dynamicznie wpisany) i Go (statycznie wpisany) obsługuje błąd typu:
def silly(a):
if a > 0:
print 'Hi'
else:
print 5 + '3'
Python sprawdza typ w czasie wykonywania, a zatem:
silly(2)
Działa idealnie dobrze i zapewnia oczekiwaną wydajność Hi
. Błąd pojawia się tylko wtedy, gdy trafi się problematyczna linia:
silly(-1)
Produkuje
TypeError: unsupported operand type(s) for +: 'int' and 'str'
ponieważ odpowiednia linia została faktycznie wykonana.
Z drugiej strony sprawdzanie typu w czasie kompilacji:
package main
import ("fmt"
)
func silly(a int) {
if (a > 0) {
fmt.Println("Hi")
} else {
fmt.Println("3" + 5)
}
}
func main() {
silly(2)
}
Powyższe nie zostanie skompilowane z następującym błędem:
invalid operation: "3" + 5 (mismatched types string and int)
runhaskell
na przykład za pomocą.
Mówiąc prosto: w języku o typie statycznym typy zmiennych są statyczne , co oznacza, że po ustawieniu zmiennej na typ nie można jej zmienić. Wynika to z faktu, że pisanie jest powiązane ze zmienną, a nie z wartością, do której się odnosi.
Na przykład w Javie:
String str = "Hello"; //variable str statically typed as string
str = 5; //would throw an error since str is supposed to be a string only
Gdzie z drugiej strony: w dynamicznie typowanym języku typy zmiennych są dynamiczne , co oznacza, że po ustawieniu zmiennej na typ, MOŻNA ją zmienić. Wynika to z faktu, że pisanie jest powiązane z wartością, którą przyjmuje, a nie z samą zmienną.
Na przykład w Pythonie:
str = "Hello" # variable str is linked to a string value
str = 5 # now it is linked to an integer value; perfectly OK
Dlatego najlepiej jest myśleć o zmiennych w dynamicznie wpisywanych językach jako o ogólnych wskaźnikach do wpisywanych wartości.
Podsumowując, wpisz opis (lub powinien był opisać) zmienne w języku, a nie w samym języku. Mogłoby to być lepiej wykorzystane jako język ze zmiennymi o typie statycznym w porównaniu do języka o zmiennych o typie dynamicznym IMHO.
Języki o typie statycznym są ogólnie językami kompilowanymi, dlatego kompilatory sprawdzają typy (czy to ma sens, prawda, ponieważ typów nie można później zmieniać w czasie wykonywania).
Języki o typie dynamicznym są na ogół interpretowane, dlatego sprawdzanie typu (jeśli istnieje) odbywa się w czasie wykonywania, gdy są używane. To oczywiście wiąże się z pewnym kosztem wydajności i jest jednym z powodów, dla których języki dynamiczne (np. Python, ruby, php) nie skalują się tak dobrze, jak te pisane na maszynie (java, c # itp.). Z innej perspektywy, języki o typie statycznym wiążą się z większymi kosztami początkowymi: sprawiają, że zwykle piszesz więcej kodu, trudniejszy kod. Ale to się później opłaca.
Dobrą rzeczą jest to, że obie strony pożyczają cechy z drugiej strony. Języki pisane zawierają bardziej dynamiczne funkcje, np. Generyczne i biblioteki dynamiczne w języku C #, a języki dynamiczne obejmują więcej sprawdzania typów, np. Adnotacje typów w pythonie lub wariant HACK PHP, które zwykle nie są rdzeniem języka i można je stosować żądanie.
Jeśli chodzi o wybór technologii, żadna ze stron nie ma wewnętrznej przewagi nad drugą. To tylko kwestia preferencji, czy chcesz na początku mieć większą kontrolę, czy elastyczność. po prostu wybierz odpowiednie narzędzie do pracy i sprawdź, co jest dostępne przeciwnie, przed rozważeniem zmiany.
http://en.wikipedia.org/wiki/Type_system
Pisanie statyczne
Mówi się, że język programowania używa pisania statycznego, gdy sprawdzanie typu odbywa się w czasie kompilacji, a nie w czasie wykonywania. W typowaniu statycznym typy są powiązane ze zmiennymi, a nie wartościami. Języki o typie statycznym obejmują Ada, C, C ++, C #, JADE, Java, Fortran, Haskell, ML, Pascal, Perl (w odniesieniu do rozróżniania skalarów, tablic, skrótów i podprogramów) oraz Scala. Wpisywanie statyczne jest ograniczoną formą weryfikacji programu (patrz bezpieczeństwo typu): w związku z tym umożliwia wykrycie wielu błędów typu na wczesnym etapie cyklu programowania. Statyczne moduły sprawdzające oceniają tylko informacje o typie, które można określić w czasie kompilacji, ale są w stanie zweryfikować, czy sprawdzone warunki obowiązują dla wszystkich możliwych wykonań programu, co eliminuje potrzebę powtarzania sprawdzania typu przy każdym uruchomieniu programu. Wykonanie programu może być również bardziej wydajne (tj. Szybsze lub zajmujące mniej pamięci) przez pominięcie kontroli typu środowiska wykonawczego i włączenie innych optymalizacji.
Ponieważ oceniają informacje o typie podczas kompilacji, a zatem brakuje informacji o typie, które są dostępne tylko w czasie wykonywania, statyczne sprawdzanie typów jest zachowawcze. Odrzucą niektóre programy, które mogą być dobrze zachowane w czasie wykonywania, ale których nie można określić statycznie jako dobrze wpisane. Na przykład, nawet jeśli wyrażenie zawsze sprawdza wartość true w czasie wykonywania, program zawierający kod
if <complex test> then 42 else <type error>
zostanie odrzucony jako źle wpisany, ponieważ analiza statyczna nie może ustalić, że gałąź else nie zostanie podjęta. [1] Zachowawcze zachowanie sprawdzania typu statycznego jest korzystne, gdy rzadko jest sprawdzane jako fałsz: Sprawdzanie typu statycznego może wykryć błędy typu w rzadko używanych ścieżkach kodu. Bez statycznego sprawdzania typu nawet testy pokrycia kodu ze 100% pokryciem kodu mogą nie być w stanie znaleźć takich błędów typu. Testy pokrycia kodu mogą nie wykryć takich błędów typu, ponieważ należy wziąć pod uwagę kombinację wszystkich miejsc, w których tworzone są wartości i wszystkich miejsc, w których stosowana jest określona wartość.
Najczęściej używane statycznie pisane języki nie są bezpieczne pod względem formalnym. Mają „luki” w specyfikacji języka programowania, dzięki czemu programiści mogą pisać kod, który omija weryfikację przeprowadzaną przez moduł sprawdzania typu statycznego, a zatem rozwiązuje szerszy zakres problemów. Na przykład Java i większość języków w stylu C ma funkcję znakowania, a Haskell ma takie funkcje, jak niebezpiecznePerformIO: takie operacje mogą być niebezpieczne w czasie wykonywania, ponieważ mogą powodować niepożądane zachowanie z powodu nieprawidłowego wpisywania wartości podczas działania programu.
Pisanie dynamiczne
Mówi się, że język programowania jest wpisywany dynamicznie lub po prostu „dynamicznie”, gdy większość jego sprawdzania typów jest wykonywana w czasie wykonywania, a nie w czasie kompilacji. W typowaniu dynamicznym typy są powiązane z wartościami, a nie zmiennymi. Dynamicznie wpisywane języki to Groovy, JavaScript, Lisp, Lua, Objective-C, Perl (w odniesieniu do typów zdefiniowanych przez użytkownika, ale nie wbudowanych), PHP, Prolog, Python, Ruby, Smalltalk i Tcl. W porównaniu do pisania statycznego, pisanie dynamiczne może być bardziej elastyczne (np. Zezwalając programom na generowanie typów i funkcji na podstawie danych wykonawczych), choć kosztem mniejszej liczby gwarancji a priori. Wynika to z faktu, że język o typie dynamicznym akceptuje i próbuje uruchomić niektóre programy, które mogą zostać uznane za nieprawidłowe przez moduł sprawdzania typu statycznego.
Pisanie dynamiczne może powodować błędy typu środowisko wykonawcze - to znaczy, że w czasie wykonywania wartość może mieć nieoczekiwany typ i stosowana jest operacja nonsensowna dla tego typu. Ta operacja może nastąpić długo po miejscu, w którym popełniono błąd programowy - to znaczy miejscu, w którym niewłaściwy typ danych przeszedł do miejsca, w którym nie powinien być. Utrudnia to zlokalizowanie błędu.
Dynamicznie typowane systemy językowe, w porównaniu do swoich statycznie wpisywanych kuzynów, dokonują mniej kontroli kodu źródłowego w czasie kompilacji (ale sprawdzą na przykład, czy program jest poprawny pod względem składniowym). Kontrole w czasie wykonywania mogą być potencjalnie bardziej skomplikowane, ponieważ mogą wykorzystywać informacje dynamiczne, a także wszelkie informacje, które były obecne podczas kompilacji. Z drugiej strony, kontrole środowiska wykonawczego potwierdzają jedynie, że warunki obowiązują przy konkretnym wykonaniu programu, a kontrole te są powtarzane przy każdym wykonaniu programu.
Rozwój w językach dynamicznie typowanych jest często wspierany przez praktyki programowania, takie jak testy jednostkowe. Testowanie jest kluczową praktyką w profesjonalnym tworzeniu oprogramowania i jest szczególnie ważne w dynamicznie pisanych językach. W praktyce testy przeprowadzone w celu zapewnienia poprawnego działania programu mogą wykryć znacznie szerszy zakres błędów niż statyczne sprawdzanie typu, ale przeciwnie, nie mogą tak kompleksowo wyszukiwać błędów, które są w stanie wykryć zarówno testowanie, jak i statyczne sprawdzanie typu. Testowanie może zostać włączone do cyklu kompilacji oprogramowania, w którym to przypadku można go traktować jako kontrolę czasu kompilacji, ponieważ użytkownik programu nie będzie musiał ręcznie uruchamiać takich testów.
Bibliografia
- Pierce, Benjamin (2002). Rodzaje i języki programowania. MIT Naciśnij. ISBN 0-262-16209-1.
myObject[remoteDataName]
. : Wtedy nie ma sposobu, aby dowiedzieć się, którą właściwość wybierze, a nawet czy w ogóle jest to ważna właściwość.
Terminologia „dynamicznie wpisywana” jest niestety myląca. Wszystkie języki są typowane statycznie, a typy są właściwościami wyrażeń (a nie wartości, jak niektórzy sądzą). Jednak niektóre języki mają tylko jeden typ. Są to tak zwane języki jednoznaczne. Jednym z przykładów takiego języka jest niepisany rachunek lambda.
W niepisanym rachunku lambda wszystkie terminy są terminami lambda, a jedyną operacją, którą można wykonać dla danego terminu, jest zastosowanie go do innego terminu. Dlatego wszystkie operacje zawsze prowadzą do nieskończonej rekurencji lub do wyrażenia lambda, ale nigdy nie sygnalizują błędu.
Jednakże, byliśmy w celu wzmocnienia bez typu rachunku lambda z pierwotnych liczb i operacji arytmetycznych, to może wykonywać operacje nonsensownych takie dodanie dwóch warunków lambda (λx.x) + (λy.y)
. Można argumentować, że jedyną rozsądną rzeczą jest zasygnalizowanie błędu, gdy tak się stanie, ale aby móc to zrobić, każdą wartość należy oznaczyć wskaźnikiem wskazującym, czy jest to termin lambda czy liczba. Operator dodawania sprawdzi następnie, czy rzeczywiście oba argumenty są oznaczone liczbami, a jeśli nie są, zasygnalizuje błąd. Pamiętaj, że te tagi nie są typami, ponieważ typy są właściwościami programów, a nie wartości wytwarzanych przez te programy.
Język, który to robi, jest nazywany typowaniem dynamicznym.
Wszystkie języki, takie jak JavaScript, Python i Ruby, są jednoznaczne. Znów typeof
operator w JavaScript i type
funkcja w Pythonie mają mylące nazwy; zwracają tagi związane z operandami, a nie ich typy. Podobnie dynamic_cast
w C ++ i instanceof
Java nie sprawdzaj typów.
„Po przetłumaczeniu kodu źródłowego”
„Po sprawdzeniu typów”
5 + '3'
jest przykładem błędu typu w silnie typowanych językach, takich jak Go i Python, ponieważ nie pozwalają one na „przymus typu” -> możliwość zmiany wartości w pewnych kontekstach, takich jak łączenie dwóch typów. Słabo wpisane języki, takie jak JavaScript, nie zgłaszają błędu pisowni (powoduje w '53'
).
Definicje „statyczne i skompilowane” oraz „dynamiczne i zinterpretowane” są dość podobne ... ale pamiętaj, że to „gdy typy są sprawdzane” vs. „kiedy kod źródłowy jest tłumaczony”.
Otrzymasz ten sam błąd typu, niezależnie od tego, czy język jest skompilowany czy zinterpretowany ! Musisz oddzielić te warunki koncepcyjnie.
Dynamiczny, zinterpretowany
def silly(a):
if a > 0:
print 'Hi'
else:
print 5 + '3'
silly(2)
Ponieważ Python jest interpretowany i dynamicznie wpisywany, tłumaczy tylko i sprawdza kod, na którym jest wykonywany. else
Blok nie realizuje, więc 5 + '3'
się nawet nie spojrzał!
Co jeśli został wpisany statycznie?
Błąd typu zostałby zgłoszony przed uruchomieniem kodu. Nadal wykonuje sprawdzenie typu przed uruchomieniem, nawet jeśli jest interpretowane.
Co jeśli zostałby skompilowany?
else
Blok byłyby tłumaczone / spojrzał przed okresie czasu, ale ponieważ jest dynamicznie wpisany nie byłoby wyrzucić błąd! Dynamicznie wpisywane języki nie sprawdzają typów aż do wykonania, a linia ta nigdy się nie wykonuje.
Statyczne, skompilowane
package main
import ("fmt"
)
func silly(a int) {
if (a > 0) {
fmt.Println("Hi")
} else {
fmt.Println("3" + 5)
}
}
func main() {
silly(2)
}
Typy są sprawdzane przed uruchomieniem (statyczne), a błąd typu jest natychmiast wychwytywany! Typy będą nadal sprawdzane przed uruchomieniem, jeśli zostaną zinterpretowane z takim samym wynikiem. Gdyby był dynamiczny, nie generowałby żadnych błędów, nawet jeśli kod byłby sprawdzany podczas kompilacji.
Skompilowany język będzie miał lepszą wydajność w czasie wykonywania, jeśli zostanie wpisany statycznie (w porównaniu z dynamicznym); znajomość typów pozwala na optymalizację kodu maszynowego.
Języki o typie statycznym mają lepszą wydajność w czasie wykonywania wewnętrznie, ponieważ nie trzeba dynamicznie sprawdzać typów podczas wykonywania (sprawdza przed uruchomieniem).
Podobnie, skompilowane języki są szybsze w czasie wykonywania, ponieważ kod został już przetłumaczony, zamiast konieczności „interpretowania” / tłumaczenia w locie.
Pamiętaj, że zarówno skompilowane, jak i statycznie wpisane języki będą miały opóźnienie przed uruchomieniem odpowiednio dla tłumaczenia i sprawdzania typu.
Wpisywanie statyczne wykrywa błędy wcześnie, zamiast znajdować je podczas wykonywania (szczególnie przydatne w przypadku długich programów). Jest bardziej „ścisły”, ponieważ nie pozwala na błędy nigdzie w twoim programie i często zapobiega zmianie typów zmiennych, co dodatkowo chroni przed niezamierzonymi błędami.
num = 2
num = '3' // ERROR
Pisanie dynamiczne jest bardziej elastyczne, co niektórzy doceniają. Zwykle pozwala zmiennym zmieniać typy, co może powodować nieoczekiwane błędy.
Języki o typie statycznym : każda zmienna i wyrażenie jest już znane w czasie kompilacji.
( int a;
a może przyjmować tylko wartości typu całkowitego w czasie wykonywania)
Przykłady: C, C ++, Java
Języki o typie dynamicznym : zmienne mogą przyjmować różne wartości w czasie wykonywania, a ich typ jest definiowany w czasie wykonywania.
( var a;
a może przyjmować dowolne wartości w czasie wykonywania)
Przykłady: Ruby, Python.
Sprawdzanie typów języków wpisywanych statycznie w czasie kompilacji, a typ NIE może ulec zmianie. (Nie przejmuj się komentarzami do rzutowania, tworzona jest nowa zmienna / odwołanie).
Dynamiczne sprawdzanie typów języków w czasie wykonywania, a typ zmiennej MOŻE być zmieniany w czasie wykonywania.
Słodkie i proste definicje, ale dopasowane do potrzeb: Języki o typie statycznym wiążą typ ze zmienną dla całego jego zakresu (Seg: SCALA) Języki o typie dynamicznym wiążą typ z rzeczywistą wartością, do której odwołuje się zmienna.
Języki o typie statycznym, takie jak C ++, Java i języki o typie dynamicznym, takie jak Python, różnią się tylko pod względem wykonania typu zmiennej. Języki o typie statycznym mają statyczny typ danych dla zmiennej, tutaj typ danych jest sprawdzany podczas kompilacji, więc debugowanie jest znacznie prostsze ... podczas gdy typowanie dynamiczne języki o nie robią tego samego, sprawdzany jest typ danych, który wykonuje program, a zatem debugowanie jest trochę trudne.
Co więcej, mają one bardzo małą różnicę i mogą być powiązane z językami silnie i słabo typowanymi . Język o silnym typie nie pozwala na użycie jednego typu jako drugiego np. C i C ++ ... podczas gdy słabo wpisane języki pozwalają np. Na phython
Wpisany statycznie
Typy są sprawdzane przed uruchomieniem, więc błędy mogą zostać wykryte wcześniej.
Przykłady = c ++
Dynamicznie wpisane
Typy są sprawdzane podczas wykonywania.
Przykłady = Python
Języki o typie statycznym (kompilator rozwiązuje wywołania metod i odwołania do kompilacji):
Dynamiczne języki pisane (decyzje podejmowane podczas działania programu):
dynamicznie typowany język pomaga szybko prototypować koncepcje algorytmów bez konieczności zastanawiania się, jakie typy zmiennych należy zastosować (co jest koniecznością statycznie typowanym języku e).
Pisanie statyczne: Języki, takie jak Java i Scala, są pisane statycznie.
Zmienne muszą zostać zdefiniowane i zainicjowane, zanim zostaną użyte w kodzie.
na przykład int x; x = 10;
System.out.println (x);
Pisanie dynamiczne: Perl jest językiem pisanym dynamicznie.
Zmienne nie muszą być inicjowane, zanim zostaną użyte w kodzie.
y = 10; użyj tej zmiennej w dalszej części kodu
$
), array ( @
) i hash ( %
). Typ zmiennej w Perlu jest znany w czasie kompilacji i pozostaje taki sam przez resztę życia zmiennych.