Jakie są podstawy matematyczne dla wartości pierwszej / drugiej / trzeciej klasy w językach programowania?

19

Dodany

Właśnie znalazłem dwa powiązane pytania

/math//q/1759680/1281

/programming//a/2582804/156458


W językach programowania, od Pragmatics Programming Language Michaela Scotta

Mówi się, że wartość w języku programowania ma status pierwszej klasy, jeśli można ją przekazać jako parametr, zwrócić z podprogramu lub przypisać do zmiennej. Proste typy, takie jak liczby całkowite i znaki, są wartościami pierwszej klasy w większości języków programowania. Natomiast wartość „drugiej klasy” można przekazać jako parametr, ale nie można jej zwrócić z podprogramu ani przypisać do zmiennej, a wartości „trzeciej klasy” nie można nawet przekazać jako parametru.

Etykiety są wartościami trzeciej klasy w większości języków programowania, ale wartościami drugiej klasy w Algolu. Podprogramy wykazują największą różnorodność. Są to wartości pierwszej klasy we wszystkich funkcjonalnych językach programowania i większości języków skryptowych. Są to również wartości pierwszej klasy w języku C # i, z pewnymi ograniczeniami, w kilku innych imperatywnych językach, w tym Fortran, Modula-2 i -3, Ada 95, C i C ++. 11 Są to wartości drugiej kategorii w większości innych imperatywnych języków, a wartości trzeciej klasy w Adzie 83.

  1. Jakie są podstawy matematyczne dla wartości pierwszej / drugiej / trzeciej klasy w językach programowania?

    Terminologia przypomina mi logikę pierwszego / drugiego rzędu, ale czy są one powiązane?

  2. Wydaje mi się, że różnica między nimi polega na tym, w którym konkretnym przypadku można zastosować wartość

    • przekazany jako parametr,
    • wrócił z podprogramu lub
    • przypisany do zmiennej.

    Dlaczego konkretne przypadki są ważne, a inne przypadki nie są wymienione?

Dzięki.

Tim
źródło
23
Klasyfikowanie wartości do pierwszej / drugiej klasy jest tak bezowocne, jak klasyfikowanie języków na paradygmaty. Wszystko, co skończysz, to niejasne uogólnienia, które mylą obraz. To nie jest właściwe podejście do rozumienia języków programowania. Ważne jest zrozumienie składni, statyki i dynamiki języka, ale jest to o wiele za dużo, by je
omówić
3
Poza tym: PL / I byłbym przykładem etykiet jako pierwszej klasy. W PL / I możesz zadeklarować zmienne wpisane do etykiety, przypisać im lokalizacje kodu i goto je. Możesz również przekazać je jako parametry, a nawet utworzyć ich tablice.
Theraot
@Theraot: Być może umawiam się ze sobą, ale czy jestem jedynym, który kiedykolwiek miał do czynienia z przypisanymi i obliczonymi GOTO w FORTRAN? I nie, nie napisałem @ $%! kod, utknąłem, przeprojektowując go.
jamesqf
@jamesqf Mam świadomość przypisywania etykiet w FORTRAN i COBOL, nie, nie użyłem tego. Nie jestem pewien, jak klasyfikować tam etykiety. Jednak w przypadku tego, co przeczytałem (mam instrukcje), PL / I wykracza poza to i jestem przekonany, że etykiety są tam najwyższej klasy.
Theraot

Odpowiedzi:

45

Nie ma żadnych i jest to dość arbitralne.

Jedynym użytecznym rozróżnieniem jest pierwsza klasa i wszystkie inne. Każdy przypadek, który znajduje się w „innym” przedziale, ma swój odrębny zestaw reguł w każdym przypadku, a zbicie ich wszystkich razem nie jest zbyt pomocne. „Pierwsza klasa” oznacza „Nie musisz szukać zasad”, a „inne” to „Musisz nauczyć się zasad”.

Na przykład w C ++ poszczególne funkcje są wartościami pierwszej klasy, o ile są bezstanowe. Zestawy przeciążeniowe nie są, ale są lambdas. W języku C # funkcje są na ogół pierwszorzędnymi wartościami, ale istnieją pewne niezręczne przypadki, które pojawiają się podczas obsługi wnioskowania typu, które uniemożliwiają im występowanie we wszystkich przypadkach.

DeadMG
źródło
10
+1, chociaż w kontekście książki takiej jak Pragmatics Programming Language, która porównuje dużą liczbę konstrukcji w dużej liczbie języków i analizuje podobieństwa, różnice i implikacje dogłębnie, myślę, że ten rodzaj skrótu może być przydatny. (Po prostu nie chodź w kółko, oczekując, że inni zrozumieją „z drugiej ręki” i „z drugiej ręki” jako konkretne rzeczy.)
ruakh
(Uh, gdzie przez „z drugiej ręki” i „z trzeciej ręki” mam oczywiście na myśli „drugiej klasy” i „trzeciej klasy”. :-P)
ruakh
3
Jeśli chcesz handlować funkcjami używanymi, lepiej zrób to w języku, w którym funkcje są pierwszorzędnymi obywatelami. ^^
5gon12eder
@ 5gon12eder: „funkcje używane” to te, które wywołujesz z bibliotek zewnętrznych, prawda?
jamesqf
11

Zgadzam się z DeadMG, ważne jest rozróżnienie między pierwszorzędnym a „wszystkim innym”. Istnieje jednak bardziej znany sposób sklasyfikowania różnicy.

Pierwszej klasy wartości to dane, pozostałe to kod. (z grubsza mówiąc: jestem pewien, że są wyjątki. Ale to naprawdę dobre przybliżenie, które dotyczy prawdziwych języków).

W niektórych językach możesz traktować kod jako dane. Znane są z tego języki funkcjonalne: niektóre z nich umożliwiają zmianę kodu uruchomionego programu (podstawa programowania genetycznego ).

Języki takie jak C i C ++ pozwalają ci wziąć adres funkcji: chociaż nie możesz ich modyfikować, możesz przekazać funkcje jako parametry do innych funkcji. C ++ ma również cukier syntaktyczny funktorów . Chodzi o to, aby stworzyć cały obiekt, który na powierzchni wydaje się zachowywać jak funkcja i może być przekazywany tak, jakby był danymi. To, co poza tym jest niższą klasą wartości, może być traktowane jako wartość pierwszej klasy.

Matematycznie myślę, że najlepszym sposobem jest pomyślenie o AST programu . Zazwyczaj każdy token ma określony typ, który może, ale nie musi być zgodny z innymi typami. Pomyśl o wartości l, wartości r i innym całym bałaganie typów wartości w C ++ . Następnie dodaj słowa kluczowe, symbole, które są funkcjami itp. Niektóre z nich mogą być wartościami pierwszej, drugiej lub trzeciej klasy, w zależności od języka.

Nie jestem pewien, czy znajomość „klasy” wartości jest tak ważna, chyba że w środowisku akademickim. W świecie rzeczywistym ważne jest, aby wiedzieć, w jaki sposób można przekazywać kod, traktując go jak dane: funktory, lambda / zamknięcia, wskaźniki funkcji itp.


źródło
2
Przykład wartości innej niż kod nie pierwszej klasy: Lua ma specjalną ...„zmienną” używaną do oznaczania parametrów funkcji vararg. Możesz używać go w wielu wyrażeniach, tak jakby to była lista wartości (np. print(...)Lub local args = {...}), ale jest kilka rzeczy, których nie możesz z tym zrobić, na przykład odwoływać się do niej w zamknięciu ( function(...) return function() return ... end end).
Pułkownik Trzydzieści Dwa
8

Denotational Semantics zapewnia matematyczne podstawy do opisu działania wartości i zmiennych w języku programowania. Zostało to tak dobrze wyjaśnione w moim Computer Sci Degree, że uzyskałem najwyższą ocenę na egzaminie Denotational Semantics, a potem zapomniałem większość z nich i nigdy nie miałem potrzeby używania go w programistyce przez 20 lat.

Możesz użyć dobrze zdefiniowanej podstawy matematycznej lub możesz użyć nieformalnej terminologii, takiej jak „status pierwszej klasy”. Dowiedziałbym się o wiele więcej, gdyby kurs był oparty na Scott's Programming Language Pragmatics, jednak formalna matematyka jest potrzebna dla kogoś, kto zrobi doktorat z projektowania języka programowania.

Jeśli przeczytasz specyfikację większości języków programowania, zauważysz dysydencki brak Denotational Semantics, jednak w większości dobrze zaprojektowanych języków był ktoś w zespole, który jest ekspertem w projektowaniu języków programowania, dlatego dobrze rozumie Denotational Semantics.

Tak więc Michearl Scott używa nieformalnej terminologii, która ma pewien związek z formalną matematyką, jednocześnie przedstawiając ten temat w sposób, z którego większość programistów może skorzystać. Jego terminologia nie jest używana przez innych ludzi, więc nie jest przydatna w komunikacji, ale daje dobrą podstawę do pytań, które powinieneś zadać, widząc nowy język programowania po raz pierwszy.

Zauważ, że Michael L. Scott jest wiodącym badaczem w dziedzinie Computer Sci, więc zrozumie i będzie bardzo szczęśliwy, korzystając z formalnej matematyki, ale podobnie jak najlepsi badacze, jest on w stanie objaśnić zastosowanie badań dla reszty z nas.

Ian
źródło
Dzięki. Które książki były używane w twojej klasie? Które książki polecasz teraz? Jestem samoukiem
Tim
@ Tim, zrobiłem klasę ponad 20 lat temu! Oczekuję, że książka Michearla Scotta jest jedną z najlepszych i obejmie więcej niż potrzebujesz, chyba że zamierzasz prowadzić badania na poziomie PHd.
Ian
3

Nie, słowo „pierwszy” w „pierwszej klasie” i „pierwszym rzędzie” oznacza różne rzeczy.

Ale tak, pojęcia „pierwszej klasy” i „pierwszego rzędu” są powiązane. Oba dotyczą klasyfikowania, które pojęcia, które język może opisać, mogą być również abstrakcyjne.

Koncepcja jest pierwszorzędna, jeśli zwykłe mechanizmy abstrakcyjne języka mogą abstrahować od tej koncepcji.

Na przykład język programowania Java może opisywać liczby całkowite, a wszystkie zwykłe mechanizmy abstrakcyjne nad liczbami całkowitymi (przyjmowanie ich jako parametrów metody, zwracanie ich jako wyników funkcji, przechowywanie ich w strukturach danych, ...) działają na liczbach całkowitych.

Pojęcie ma pierwszeństwo, jeśli nie można go użyć do abstrakcyjnej analizy samego siebie.

Na przykład, ponownie w Javie, możemy użyć metod do streszczenia pewnych rzeczy. Ale nie możemy używać metod do abstrakcji nad metodami, ponieważ nazwy metody nie można przekazać jako parametru metody. W JavaScript jest inaczej, gdzie można użyć notacji w nawiasach, aby uzyskać dostęp do właściwości obiektu według jego nazwy jako ciągu, a także można abstrakcyjnie wstawiać ciągi.

Pojęcie jest drugiego rzędu, jeśli można je wykorzystać do streszczenia samego użycia pierwszego rzędu, ale nie do zastosowań drugiego rzędu.

Na przykład w Javie możesz używać Generics do abstrakcji na typy (jak w class Foo<T> { public List<T> content; }). Nie można jednak używać rodzajów ogólnych w celu wyodrębnienia elementów ogólnych (jak w class Bar<T> { public T<Int> content; }). W Scali jest inaczej.

Pojęcie jest trzeciego rzędu, jeśli można je wykorzystać do abstrakcji na użytek samego pierwszego i drugiego rzędu, ale nie na użytek drugiego rzędu.

I tak dalej.

Wreszcie, koncepcja jest wyższego rzędu, jeśli można ją wykorzystać do abstrakcji nad samowolnym wykorzystaniem.

Podsumowanie: Jeśli funkcja abstrakcji jest pierwszorzędna, jest również wyższego rzędu. A jeśli funkcja abstrakcyjna jest pierwszego rzędu, nie może być pierwszej klasy.

Toxaris
źródło
1
Możesz jednak zrobić to List<List>w Javie. Może możesz wyjaśnić, co masz na myśli w tej części?
Polygnome
1
Słuszna uwaga. To znaczy: class Foo<A> { A<Int> ... }. To znaczy, mam na myśli użycie parametru typu ogólnego jako klasy ogólnej. Następnie Foo<List>utworzyłby instancję A<Int>do List<Int>. W Javie nie jest to możliwe. Spróbuję później edytować to w odpowiedzi.
Toxaris
Rzeczywiście, nie jest to możliwe.
Polygnome
Teraz zastąpiłem mylący List<List>przykład. Dziękujemy za zwrócenie uwagi na problem @Polygnome.
Toxaris
@Toxaris Myślę, że to tylko idiosynkratyczna terminologia, którą sam wymyśliłeś. „Pojęcie” nie jest ani pierwszym, ani drugim rzędem, logicznym kwantyfikatorem jest.
Miles Rout,
2

Jakie są podstawy matematyczne dla wartości pierwszej / drugiej / trzeciej klasy w językach programowania?

Brak, którego jestem świadomy.

Terminologia przypomina mi logikę pierwszego / drugiego rzędu, ale czy są one powiązane?

Nie całkiem.

„Klasa” elementu języka programowania to tylko skrótowy sposób myślenia o pytaniu, jakie rzeczy użytkownik mojego języka chce manipulować programowo ? Na przykład C # daje bogaty zestaw operacji do manipulowania wartościami , mniej bogaty zestaw sposobów manipulowania typami i żadnych operacji, które manipulują etykietami .

Jednak twoja intuicja, że ​​istnieje tutaj połączenie, nie jest całkowicie niewłaściwa. Analogii należy dokonać od logiki pierwszego rzędu do programowania proceduralnego oraz od logiki wyższego rzędu do programowania funkcjonalnego. Logika pierwszego rzędu dotyczy logicznej manipulacji wartościami; programowanie proceduralne dotyczy programowej manipulacji wartościami. Logika wyższego rzędu dotyczy logicznego manipulowania instrukcjami logiki , programowanie funkcjonalne dotyczy programowego manipulowania funkcjami .

Dlaczego konkretne przypadki są ważne, a inne przypadki nie są wymienione?

Będziesz musiał poprosić autora o ostateczną odpowiedź.

Nie rozwiewałoby mnie zbytnio pojęcie „klasy”. To nie jest formalnie zdefiniowana rzecz. Jest to skrót, którego używają projektanci języków, aby mówić o tym, jakie rzeczy można programowo modyfikować.

Eric Lippert
źródło
2

„Pierwszorzędna wartość” w tym kontekście jest standardową terminologią w teorii języków programowania. Pierwszorzędną wartością jest coś, czym można manipulować jako normalne wartości w języku, coś, co można obliczyć w czasie wykonywania. Oczywiście jest to definicja tautologiczna, dopóki nie zdefiniujesz semantyki dla języka, a wtedy wartość jest tym, co semantyka określa jako wartość. Celem tej koncepcji jest określenie, czym można manipulować bezpośrednio, a nie tylko pośrednio.

Na przykład w prawie każdym języku programowania liczby całkowite maszyny o ograniczonym rozmiarze (np. Liczby całkowite 8-bitowe lub liczby całkowite 32-bitowe lub liczby całkowite 64-bitowe itp.) Są wartościami pierwszorzędnymi. Możesz przechowywać je w zmiennych, przekazywać i przywracać do funkcji itp. W większości języków, ale nie w językach niskiego poziomu, takich jak asembler i C, ciągi znaków są pierwszorzędnymi wartościami - ale w C nie są, tylko ty uzyskać wskaźniki do ciągów znaków. W C łańcuchy i tablice nie są wartościami pierwszej klasy: na przykład nie można przekazać tablicy do funkcji, nie można przypisać tablicy do zmiennej tablicowej itp. W C funkcje nie są wartościami pierwszej klasy albo: nie możesz zapisać funkcji w zmiennej, tylko wskaźnik do funkcji. Natomiast ciągi i funkcje są pierwszorzędnymi wartościami w większości języków programowania wysokiego poziomu: możesz przechowywać je w ciągu itp.

Przykładem koncepcji, która nie jest najlepsza w wielu językach programowania przeznaczonych do kompilacji, są typy. W języku takim jak C lub Java typy działają w czasie kompilacji, nie można nimi manipulować za pomocą konstrukcji językowych. (Java ma także dynamiczny system typów oparty na klasach; klasy są wartościami pierwszorzędnymi poprzez odbicie). Natomiast język taki jak Python matype funkcję, która zwraca wartość reprezentującą typ jej argumentu.

Negacja „wartości pierwszej klasy” w standardowej terminologii nie jest „wartością pierwszej klasy”. Termin „wartość drugiej klasy” nie jest powszechnie używany, a „wartość trzeciej klasy” jeszcze mniej. Nie spodziewaj się ich zobaczyć poza książką. Nie ma absolutnie żadnej podstawy do zdefiniowania „drugiego” jako „można przekazać jako parametr”, a „trzeciego” jako „nie można przekazać jako parametru”, nie ma skali rzeczy, które można by sensownie numerować. Bardzo niewiele języków robi różnicę między wartościami, które można przekazać jako parametr do funkcji, a wartościami, które można przypisać do zmiennych, więc zdefiniowanie nazwy tego pojęcia nie jest użyteczne.

Gilles „SO- przestań być zły”
źródło