C oder C++ lernen?
lima-city → Forum → Programmiersprachen → C/C++ und D
beispiel
bibliothek
code
frage
grammatik
konflikt
lernen
machen
pointer
problem
programm
programmieren
programmiersprache
referenz
regel
sprache
syntax
tun
unterschied
url
-
Um mal deine Rhetorik zu imiteren: Den Begriff "rhetorische Frage" hast du schon mal gehört?kamakura schrieb:
Wie zu erwarten war, hat diese Datei die Grammatik »Attributierte EBNF«. Im Speziellen handelt es sich um eine Grammatik-Beschreibung für den Generator Coco/R. Dass du den nicht kennst wundert mich übrigens nicht.
@hackyourlife: was hat die verlinkte Datei für eine Grammatik? Definitiv nicht yacc. Und dass nur das typedef ein Problem macht, steht sogar in den Comments (ganz oben) der verlinkten Datei.
Auf eine solche Naivität trifft man selten..
Coco/R, ja. Was denn sonst??? Steht u.a. oben in der URL.
Die Frage war hier eher zu verstehen als: Warum kannst du nicht wie ein normaler Mensch auf eine gängige yacc-Grammatik verlinken?hackyourlife schrieb:
Ja. Natürlich.
Zum Problem mit dem typedef: hast du denn auch nur annähernd verstanden, was dir der Kommentar in der Datei sagen will?Sieht nicht so aus, denn sonst wüsstest du, um was es sich bei einem »LL(1)-Konflikt« handelt, und dass das nichts mit kontextsensitiv/kontextfrei zu tun hat. Du leitest mit den LL(1)-Resolvern den Parser lediglich in eine konkrete Regel, um die Uneindeutigkeit (= mehrere Regeln beginnen mit dem selben Token) zu eliminieren.
Hääh? Was willst du hier für Strohmänner abbrennen?
Da steht es doch klipp und klar: "So the grammar assumes that there is a symbol table, from where you can look up an identifier and find out whether it is a type name."/* ANSI C 89 grammar as specified in http://flash-gordon.me.uk/ansi.c.txt Processing the C grammar requires LL(1) conflict resolvers, some of which need to check whether an identifier is a type name (see IsTypeName() below). So the grammar assumes that there is a symbol table, from where you can look up an identifier and find out whether it is a type name. The language in the semantic actions here is C#, but it can easily be translated to any other language. */
Und das darfst du bei einer kontextfreien Grammatik streng genommen nicht machen. Das sollte doch offensichtlich sein.Denn das ist das einzige Problem mit dem typedef hier. Würdest du beim Parsen einen GLR-Parser nutzen (kann ebenfalls nur CFGs parsen), dann hättest du nicht einmal ein Problem mit LL(k)-Konflikten.
Tja, aber hier bleibt es bei der reinen Behauptung. Im Lichte des Kommentars (wenn man's verstanden hat) ist das wohl eben nicht möglich.Für die Sache mit dem typedef wirst du aber wohl selbst dann keinen eindeutigen Parse-Baum bekommen. Dass eine Grammatik nicht eindeutig ist, darf sein, hat aber nichts mit der Klasse der Grammatik zu tun.
Und?Ein weiteres Beispiel für uneindeutige Konstrukte ist übrigens z.B. das dangling else, und dort wirst du wohl auch nicht auf die Idee kommen zu behaupten, dass das die Klasse der Grammatik irgendwie berührt.
Weil wie jeder weiß (außer dir), das nur eine scheinbare Uneindeutigkeit ist. Und ob es möglich ist, einen LL(1)-Parser zu bauen, der damit klar kommt, würde ich schon bezweifeln.
Ach, ich komme mit deiner "Weißt du nicht ...?"-Rhetorik (in den meisten Fällen wahrscheinlich dem Umstand, dass du es selber nicht weißt, geschuldet) nicht klar. Sag was Sache ist, oder lass es bleiben.
Beitrag zuletzt geändert: 30.4.2016 13:51:36 von kamakura -
Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!
lima-city: Gratis werbefreier Webspace für deine eigene Homepage
-
kamakura schrieb:
1) Warum sollte ich das tun? Es gibt schließlich mehr als nur yacc.
Die Frage war hier eher zu verstehen als: Warum kannst du nicht wie ein normaler Mensch auf eine gängige yacc-Grammatik verlinken?
2) Weil Coco/R im Gegensatz zu yacc einen Parser im rekursiven Abstieg erzeugt.
3) Um sehr schnell zu zeigen, dass du eben nicht verstanden hast, worum es hier geht.
Und von wegen Naivität: wozu sollte ich bei dir überhaupt irgend etwas voraussetzen, wenn du doch ständig beweist, dass du entweder keine Ahnung von dem hast, wovon du sprichst (z.B. wenn du von Strohmännern redest, wenn man dir lediglich etwas erklärt, was du ganz offensichtlich nicht verstanden hast), oder du auf irgendwelchen Punkten herumreitest, die nicht (vollständig) ausgesprochen wurden, weil man annahm, dass sie »eh klar« sind (z.B. LL(k)-Konflikte haben nichts mit der Chomsky-Hierarchie zu tun)? Oder Kombinationen davon? Für mich siehst du mittlerweile einfach nur wie ein Troll aus. Als Troll hast du dein Ziel also schon erreicht, du darfst dich also wieder verziehen. Dennoch bekommst du von mir eine Antwort auf all deinen Müll, den du hier verbreitest, da leider nicht jeder hier hinreichend viel zu dem Thema weiß oder Lust hat, selbst zu recherchieren.
Falsch. Wenn du Ahnung von der Sache hättest, wüsstest du auch warum. Tipp: 1) GLR verstehen, 2) darüber nachdenken, was du gerade behauptet hast, 3) korrekte Aussage tätigen.Denn das ist das einzige Problem mit dem typedef hier. Würdest du beim Parsen einen GLR-Parser nutzen (kann ebenfalls nur CFGs parsen), dann hättest du nicht einmal ein Problem mit LL(k)-Konflikten.
Tja, aber hier bleibt es bei der reinen Behauptung. Im Lichte des Kommentars (wenn man's verstanden hat) ist das wohl eben nicht möglich.
Nichts »und«, denn um genau diesen Punkt geht es hier. Nicht mehr und nicht weniger. Du müsstest ihn nur endlich mal verstehen. Siehe Wikipedia.Für die Sache mit dem typedef wirst du aber wohl selbst dann keinen eindeutigen Parse-Baum bekommen. Dass eine Grammatik nicht eindeutig ist, darf sein, hat aber nichts mit der Klasse der Grammatik zu tun.
Und?
Ob scheinbar oder nicht, es ist in der Grammatik eine Uneindeutigkeit. Ob sie dir egal ist, weil du den Parser eben so »hinbiegst«, dass er den Baum baut, den du willst, oder nicht, macht hierbei keinen Unterschied. Das dangling else kannst du übrigens problemlos mit LL(1) parsen, dabei bekommst du aber als Ergebnis, dass das else immer zum innersten if gehört.Ein weiteres Beispiel für uneindeutige Konstrukte ist übrigens z.B. das dangling else, und dort wirst du wohl auch nicht auf die Idee kommen zu behaupten, dass das die Klasse der Grammatik irgendwie berührt.
Weil wie jeder weiß (außer dir), das nur eine scheinbare Uneindeutigkeit ist. Und ob es möglich ist, einen LL(1)-Parser zu bauen, der damit klar kommt, würde ich schon bezweifeln.
Da du offenbar immer noch nicht verstanden hast, worum es hier geht, und ständig LL(k)-Konflikte mit der Klasse lt. Chomsky verwechselst und noch nicht einmal verstanden hast, warum ein LL(k)-Konflikt kein Problem für einen GLR/LALR/LR/…-Parser ist, folgende Checklist für dich:
[ ] Du hast verstanden, was überhaupt eine CFG ist (falls nicht: *klick* → Type-2; was »non-deterministic« hier heißt musst du schon selbst rausfinden).
[ ] Du hast verstanden, was das Problem bei einem LL(k)-Konflikt ist bzw wodurch dieser entsteht (falls nicht: *klick*).
[ ] Du hast die Arbeitsweise von Parsern im rekursiven Abstieg verstanden (falls nicht: *klick*).
[ ] Du hast verstanden, warum ein LL(k)-Konflikt ein Problem für Parser im rekursiven Abstieg ist (hier darfst du ggf. selbst nachdenken).
[ ] Du hast die Arbeitsweise von Parsern nach GLR/LR/LALR/… verstanden (siehe GLR, LR, LALR).
[ ] Du hast verstanden, warum sich GLR/LR/LALR/…-Parser nicht für LL(k)-Konflikte interessieren (falls nicht: basierend auf den vorherigen Punkt selbst nachdenken).
[ ] Du hast verstanden, welche Art von Konflikten es bei LR/LALR/…-Parsern gibt (falls nicht: siehe vorherige Punkte).
Wenn du alle Punkte abhaken kannst, dann ist dir auch klar, dass:
1) eine Grammatik Uneindeutigkeiten haben kann, da es für die Syntaxprüfung nicht nötig ist zu wissen, welche Regel wofür angewandt wurde. Und eine Grammatik sagt schließlich nicht mehr, als »Diese Zeichenkette liegt in der Sprache«, oder »Diese Zeichenkette liegt nicht in der Sprache« (bzw um genau zu sein: die Grammatik generiert alle Strings, die in der Sprache liegen). Nicht mehr. Und auch nicht weniger.
2) Parser im rekursiven Abstieg wissen müssen, in welche Regel sie als nächstes springen müssen, und immer nur eine Regel verarbeiten können, während LR/LALR/… mehrere Regeln gleichzeitig verfolgt, bis er sich sicher ist, um welche Regel es sich handelt.
3) yacc im Gegensatz zu Coco/R Parser nach LALR generiert, welche kein Problem mit LL(k)-Konflikten, sondern Probleme bei shift/reduce-Konflikten o.ä. haben.
Glaubst du nicht? Sieh selbst: C89 oder C11. Natürlich wirst du jetzt fragen: »und was ist ein shift/reduce-Konflikt?«, aber hierfür verweise ich auf Google, denn ich habe keine Lust mehr, Leuten etwas zu erklären, wenn sie eh glauben, selbst alles besser zu wissen. Und wegen »/* identifiers must be flagged as TYPEDEF_NAME */«: auch hier geht es wieder darum, einen eindeutigen Parse-Baum zu erzeugen. Sparst du dir das, bekommst du einen reduce/reduce-Konflikt, und für
wäre eine Deklaration oder ein Ausruck möglich.a * b;
Und noch ein Beispiel (bzw dein eigenes sogar):// typedef int a; // int a = 1, b = 2; void f(void) { a * b; }
Ist dieser Codeschnipsel syntaktisch korrekt? Natürlich könnte es sich, abhängig davon, welche Zeile auskommentiert war, um eine Deklaration handeln, genauso wie es sich sonst um einen Ausdruck handeln kann. Aber ist deshalb eine von beiden Varianten syntaktisch falsch? Nein. Also: egal welche Regel der Parser hier nutzt, er kommt zum richtigen Ergebnis: kein Syntaxfehler. Beim Parser in einem Compiler interessiert dich syntaktische Korrektheit aber nur ein wenig, wichtiger ist dir, welche Bedeutung dieses Konstrukt hat. Und da es hier eine Uneindeutigkeit gibt, hast du damit ein Problem, da du entweder einen Ausdruck oder eine Deklaration vom Parser bekommst. Das löst du bei einem Parser laut rekursiven Abstieg (da es sich hier gleichzeitig um einen LL(1)-Konflikt handelt (sowohl Deklaration als auch Ausdruck kann mit Identifier anfangen)), indem du einen LL(1)-Konfliktlöser einbaust. Und genau das sagt der Kommentar in der ursprünglich verlinkten ATG.
Um das kurz per vereinfachter Grammatik zu zeigen:
Welche Regel nimmst du nun instatement = ( decl | expr ) ";"; decl = ident { "*" } ident; expr = ident "*" ident;
, wenn dustatement
parsen willst? Du wirst feststellen: es ist völlig egal.a * b;
Aber ist das jetzt eine kontextsensitive Grammatik? Ja, da eine kontextfreie Grammatik auch eine kontextsensitive ist, in diesem Fall wäre das allerdings sogar eine reguläre Sprache (ich nehme jetzt an dieser Stelle trotzdem einfach mal an, dass du zumindest die Beziehung der Klassen zu einander kennst, und die Bedeutung des Unterschiedes zwischen »Grammatik« und »Sprache« in meiner Wortwahl)
Wie man zeigen könnte, dass es sich hier wirklich um eine reguläre Sprache handelt? Grammatik Umformulieren:
Genau das könntest du auch mit der Grammatik für C tun, und du würdest feststellen: kontextfrei, aber nicht hilfreich für einen Parser, der dein Programm parsen soll. Für die formale Eigenschaft »kontextfrei« interessiert aber niemanden, ob man damit Parser bauen könnte. Und somit ist das, was du ursprünglich behauptet hast (»allein durch typedef wird die Sprache C kontextsensitiv«) schlicht falsch. Die Sprache C ist auch ohne typedef kontextsensitiv (wegen typisierten Variablen), und die Sprache der Syntax von C ist kontextfrei, unabhängig von typedef.statement = ident { "*" } ident ";";
Namen im Scanner auflösen oder Konfliktresolver wie bei der verlinkten ATG sind übrigens nicht die einzigen Lösungsmöglichkeiten: du könntest auch eine Regel »typedefdecl-or-mul« o.ä. machen, dann kannst du das im Semantikanschluss prüfen. Ergebnis: gar kein Konflikt mehr in der Grammatik.
Sag was Sache ist, oder lass es bleiben.
Das passiert schon die ganze Zeit. Nur bist du offensichtlich nicht in der Lage, den Ausführungen bis zu Ende zu folgen und sie auch zu verstehen. Du solltest es auch endlich unterlassen, ständig von Dingen zu reden, von denen du offensichtlich keine Ahnung hast, und dann so tun, als wärst du allwissend, während angeblich alle anderen Unsinn reden.
Dass du ein Problem mit »Weißt du nicht …?« hast, könnte daran liegen, dass du eben nicht weißt
annihilus schrieb:
Du hast doch hier selbst erkannt, was ich meinte:
Das ist eben, was mich an solchen Argumentationen immer ärgert. Ja, Containerklassen haben (logischerweise) gegenüber der minimalistischen Array-Lösung mehr Overhead. Aber nein, das macht sie nicht sofort "unsinnig ineffizient"! Tatsächlich, und das war mein Argument, wird man den Unterschied im Regelfall nicht mal merken, wenn man nicht gerade mit hunderten von Matrizen jonglieren muss... und das muss eine Anwendung in der Regel einfach nicht.Hier überwiegt meiner Ansicht nach dann ganz klar der Vorteil der komfortableren Anwendbarkeit und Sicherheit.
Also: nach solchen Regeln immer, auch wenn unnötig, hoch abstrakte Dinge nutzen, ist nicht sinnvoll, genauso, wie immer nur ganz low-level-Konstrukte nutzen, weil sie eventuell schneller sind. Eh klar. Nur nach einem Statement wie »was juckt mich schon Performance« (und so las sich das Ursprüngliche von dir), musste sowas einfach kommen
Igitt, er hat Java VM gesagt! So ein Rotz kann doch nur unsicher und langsam sein! :)
Das hört man doch immer wieder, genauso wie man das doch auch bei sysvinit vs systemd usw hört. Aber wen interessiert sowas, wenn er ernsthaft damit zu tun hat und es dadurch handfeste Vorteile gibt?
Spaß beiseite, von so manchem C- oder C++-Entwickler wirst du diesen Einwand garantiert hören.
Und ganz generell: Selbst wenn diese oder eine beliebige andere neue Sprache das objektiv beste auf Gottes grüner Wiese wäre, wäre das noch lange kein Garant für Erfolg.
Das ist wohl auch klar. Aber wenn weit verbreitete Sprachen neue Features direkt im Mainstream bekommen (so wie es hier aussieht), dann gibt es zumindest größere Chancen auf Verbreitung. Das Beispiel von mir ist außerdem nur eine Erweiterung, um bestehende Sprachen besser mischen zu können, sodass man also sowas wie »in meinem C-Programm schreib ich einen performancekritischen Teil in Assembler« auch auf höherem Niveau ala »in meinem Java-Programm schreib ich den performancekritischen Teil in C« machen kann, ohne unnötig zusätzliche Komplexität und Performanceverluste (JNI? *schauder*) dadurch dadurch zu bekommen. Oder gar noch höher ala »in meinem Python-Programm schreib ich einen performancekritischen Teil in Java« oder »in meinem Python-Programm nutz ich eine externe Java-Only-Bibliothek«.
Angesichts dieser Tatsache antworte ich auf die Frage heutzutage meistens "C#". Richtig happy macht mich das nicht. Nichts würd mich mehr freuen, als wenn es bald mal wieder eine Sprache gäbe, die man guten Gewissens jedem Anfänger empfehlen kann.
Ich würde wohl Python empfehlen, da diese Sprache sehr viel erlaubt und dabei einfach ist (z.B.: man braucht OO? Dann macht man OO. Braucht man es nicht, dann macht man es auch nicht; Listen usw? extrem simpel in der Handhabung; Lambdas? Schon ewig vorhanden; usw), selbst die Sprache schon zu halbwegs lesbarem Code zwingt (wegen Einrückungen), es eine mächtige Core-Bibliothek und eine Unmenge an weiteren Bibliotheken gibt und es dennoch praktisch nichts gibt, was man nicht mit Python tun kann (= man lernt eine Sprache, die man auch später noch sinnvoll nutzen/einsetzen kann). Und weil es außerdem unabhängig von MS ist und (fast) überall brauchbar läuft (im Gegensatz zu C#).
Andere Leute würden wohl Lua oder Java empfehlen, aber ob das je eine bessere Wahl für einen Anfänger ist, ist die Frage. Entweder wird man mit vielen Konzepten erschlagen (Java ohne OO? Gibts nicht), oder man hat eine Sprache, die nur für Spezialanwendungen wirklich eingesetzt wird (Lua? Findet man wohl eher für Scripting in Spielen).
Wenn man aber schon eine Sprache kann, und sich zwischen C und C++ entscheiden muss, dann ist das wieder etwas ganz anderes.
Beitrag zuletzt geändert: 1.5.2016 4:16:48 von hackyourlife -
hackyourlife schrieb:
Nur nach einem Statement wie »was juckt mich schon Performance« (und so las sich das Ursprüngliche von dir), musste sowas einfach kommen
OK, so krass hatte ich es natürlich nicht gemeint. :)
Es ist gerade eine der Stärken von C++, dass man (innerhalb ein- und derselben Sprache) je nach Bedarf zwischen high- und low-Level wechseln kann. Einen Anfänger wird das allerdings eher verwirren als dass es ihm hilft. Da sind Sprachen besser, für die es für jedes Problem nur einen bis maximal zwei "Königswege" gibt. IMHO.
hackyourlife schrieb:
Ich würde wohl Python empfehlen, da diese Sprache sehr viel erlaubt und dabei einfach ist (z.B.: man braucht OO? Dann macht man OO. Braucht man es nicht, dann macht man es auch nicht; Listen usw? extrem simpel in der Handhabung; Lambdas? Schon ewig vorhanden; usw)...
Das mit der "optionalen OO" sehe ich persönlich eher problematisch. Es verleitet nämlich gerade Anfänger dazu, OO-Konzepte nach Lust und Laune mal anzuwenden und mal nicht, und so ein Mix ist extrem kontraproduktiv. "Ganz oder gar nicht" ist nach meinem Dafürhalten besser. Wobei ich natürlich zugeben muss: Ist bei (Object) Pascal genauso. Bei C++ sowieso (wofür kamakura ja ein paar 1A-Beispiele abgeliefert hat).
Generell zu Python: Die Einsteigerfreundlichkeit wird da immer sehr gerühmt, doch mit den Inkompatiblitäten von Version 3 zu 2 hat man sich IMO selbst enorm ins Knie geschossen.
Aus eigener Erfahrung beurteile ich das Erlernen einer neuer Sprache als enorm frustig, wenn man für ein bestimmtes Problem nach einer Lösung googelt, vermeintlich auch eine findet, aber diese dann doch aus unerfindlichen Gründen nicht funktioniert.
In diesem Fall eben, weil man dummerweise die falsche Version hat - und ein entsprechender Hinweis entweder übersehen wurde oder gar nicht vorhanden war, was auch oft genug passiert.
(Ähnlicher Effekt übrigens wenn selbst ausgewiesene "Einsteiger"-Bücher für C und C++ sich aus Platzgründen die #includes sparen... da krieg ich Zustände wenn ich sowas seh, ein Anfänger kommt nie drauf warum das dann "bei ihm nicht funktioniert").
-
hackyourlife schrieb:
Da regt sich aber jemand auf.
kamakura schrieb:
1) Warum sollte ich das tun? Es gibt schließlich mehr als nur yacc.
Die Frage war hier eher zu verstehen als: Warum kannst du nicht wie ein normaler Mensch auf eine gängige yacc-Grammatik verlinken?
2) Weil Coco/R im Gegensatz zu yacc einen Parser im rekursiven Abstieg erzeugt.
3) Um sehr schnell zu zeigen, dass du eben nicht verstanden hast, worum es hier geht.
Und von wegen Naivität: wozu sollte ich bei dir überhaupt irgend etwas voraussetzen, wenn du doch ständig beweist, dass du entweder keine Ahnung von dem hast, wovon du sprichst (z.B. wenn du von Strohmännern redest, wenn man dir lediglich etwas erklärt, was du ganz offensichtlich nicht verstanden hast), oder du auf irgendwelchen Punkten herumreitest, die nicht (vollständig) ausgesprochen wurden, weil man annahm, dass sie »eh klar« sind (z.B. LL(k)-Konflikte haben nichts mit der Chomsky-Hierarchie zu tun)? Oder Kombinationen davon? Für mich siehst du mittlerweile einfach nur wie ein Troll aus. Als Troll hast du dein Ziel also schon erreicht, du darfst dich also wieder verziehen. Dennoch bekommst du von mir eine Antwort auf all deinen Müll, den du hier verbreitest, da leider nicht jeder hier hinreichend viel zu dem Thema weiß oder Lust hat, selbst zu recherchieren.
Geht es noch diffuser?
Falsch. Wenn du Ahnung von der Sache hättest, wüsstest du auch warum. Tipp: 1) GLR verstehen, 2) darüber nachdenken, was du gerade behauptet hast, 3) korrekte Aussage tätigen.Denn das ist das einzige Problem mit dem typedef hier. Würdest du beim Parsen einen GLR-Parser nutzen (kann ebenfalls nur CFGs parsen), dann hättest du nicht einmal ein Problem mit LL(k)-Konflikten.
Tja, aber hier bleibt es bei der reinen Behauptung. Im Lichte des Kommentars (wenn man's verstanden hat) ist das wohl eben nicht möglich.
Offensichtlich liest du dir die Links, die du hier postest, nicht mal durch. Erster (!) Satz: "In computer science, an ambiguous grammar is a context-free grammar ..."
Nichts »und«, denn um genau diesen Punkt geht es hier. Nicht mehr und nicht weniger. Du müsstest ihn nur endlich mal verstehen. Siehe Wikipedia.Für die Sache mit dem typedef wirst du aber wohl selbst dann keinen eindeutigen Parse-Baum bekommen. Dass eine Grammatik nicht eindeutig ist, darf sein, hat aber nichts mit der Klasse der Grammatik zu tun.
Und?
Also hat es trivialerweise was mit der Klasse zu tun.
Ging es hier nicht um C? C ist doch nach deiner eigenen Aussage (weil du ja die "Theoretische Informatik"-Definition von "kontextfrei" verwendest) sowieso nicht kontextfrei. Was soll das also? "Uneindeutige Grammatik" ist per definitionem also schon auf C nicht anwendbar.
Ob scheinbar oder nicht, es ist in der Grammatik eine Uneindeutigkeit. Ob sie dir egal ist, weil du den Parser eben so »hinbiegst«, dass er den Baum baut, den du willst, oder nicht, macht hierbei keinen Unterschied.Ein weiteres Beispiel für uneindeutige Konstrukte ist übrigens z.B. das dangling else, und dort wirst du wohl auch nicht auf die Idee kommen zu behaupten, dass das die Klasse der Grammatik irgendwie berührt.
Weil wie jeder weiß (außer dir), das nur eine scheinbare Uneindeutigkeit ist. Und ob es möglich ist, einen LL(1)-Parser zu bauen, der damit klar kommt, würde ich schon bezweifeln.Das dangling else kannst du übrigens problemlos mit LL(1) parsen, dabei bekommst du aber als Ergebnis, dass das else immer zum innersten if gehört.
Dafür, dass du so extrem pedantisch bist, schreibst du reichlich viel Unsinn.
Bei Code wie folgendemif (expr1) if(expr2) f(); if (expr1) if(expr2) f(); else g();
ist klar, dass der Parser nicht, ohne einen Token vorauszuschauen, wissen kann, ob das Semikolon nach dem
zum ersten if (Zeile 1) oder zum zweiten (Zeile 2) gehört.f()
Da du offenbar immer noch nicht verstanden hast, worum es hier geht, und ständig LL(k)-Konflikte mit der Klasse lt. Chomsky verwechselst und noch nicht einmal verstanden hast, warum ein LL(k)-Konflikt kein Problem für einen GLR/LALR/LR/…-Parser ist, folgende Checklist für dich:
Vielen Dank. Wäre nicht nötig gewesen. Ich verstehe sowieso alles.
[ ] Du hast verstanden, was überhaupt eine CFG ist (falls nicht: *klick* → Type-2; was »non-deterministic« hier heißt musst du schon selbst rausfinden).
[ ] Du hast verstanden, was das Problem bei einem LL(k)-Konflikt ist bzw wodurch dieser entsteht (falls nicht: *klick*).
[ ] Du hast die Arbeitsweise von Parsern im rekursiven Abstieg verstanden (falls nicht: *klick*).
[ ] Du hast verstanden, warum ein LL(k)-Konflikt ein Problem für Parser im rekursiven Abstieg ist (hier darfst du ggf. selbst nachdenken).
[ ] Du hast die Arbeitsweise von Parsern nach GLR/LR/LALR/… verstanden (siehe GLR, LR, LALR).
[ ] Du hast verstanden, warum sich GLR/LR/LALR/…-Parser nicht für LL(k)-Konflikte interessieren (falls nicht: basierend auf den vorherigen Punkt selbst nachdenken).
[ ] Du hast verstanden, welche Art von Konflikten es bei LR/LALR/…-Parsern gibt (falls nicht: siehe vorherige Punkte).Wenn du alle Punkte abhaken kannst, dann ist dir auch klar, dass:
Off-Topic!
1) eine Grammatik Uneindeutigkeiten haben kann, da es für die Syntaxprüfung nicht nötig ist zu wissen, welche Regel wofür angewandt wurde. Und eine Grammatik sagt schließlich nicht mehr, als »Diese Zeichenkette liegt in der Sprache«, oder »Diese Zeichenkette liegt nicht in der Sprache« (bzw um genau zu sein: die Grammatik generiert alle Strings, die in der Sprache liegen). Nicht mehr. Und auch nicht weniger.
2) Parser im rekursiven Abstieg wissen müssen, in welche Regel sie als nächstes springen müssen, und immer nur eine Regel verarbeiten können, während LR/LALR/… mehrere Regeln gleichzeitig verfolgt, bis er sich sicher ist, um welche Regel es sich handelt.
3) yacc im Gegensatz zu Coco/R Parser nach LALR generiert, welche kein Problem mit LL(k)-Konflikten, sondern Probleme bei shift/reduce-Konflikten o.ä. haben.
Glaubst du nicht? Sieh selbst: C89 oder C11. Natürlich wirst du jetzt fragen: »und was ist ein shift/reduce-Konflikt?«, aber hierfür verweise ich auf Google, denn ich habe keine Lust mehr, Leuten etwas zu erklären, wenn sie eh glauben, selbst alles besser zu wissen. Und wegen »/* identifiers must be flagged as TYPEDEF_NAME */«: auch hier geht es wieder darum, einen eindeutigen Parse-Baum zu erzeugen. Sparst du dir das, bekommst du einen reduce/reduce-Konflikt, und für
wäre eine Deklaration oder ein Ausruck möglich.a * b;
Und noch ein Beispiel (bzw dein eigenes sogar):
Ist ja interessant.// typedef int a; // int a = 1, b = 2; void f(void) { a * b; }
Ist dieser Codeschnipsel syntaktisch korrekt? Natürlich könnte es sich, abhängig davon, welche Zeile auskommentiert war, um eine Deklaration handeln, genauso wie es sich sonst um einen Ausdruck handeln kann. Aber ist deshalb eine von beiden Varianten syntaktisch falsch? Nein. Also: egal welche Regel der Parser hier nutzt, er kommt zum richtigen Ergebnis: kein Syntaxfehler. Beim Parser in einem Compiler interessiert dich syntaktische Korrektheit aber nur ein wenig, wichtiger ist dir, welche Bedeutung dieses Konstrukt hat. Und da es hier eine Uneindeutigkeit gibt, hast du damit ein Problem, da du entweder einen Ausdruck oder eine Deklaration vom Parser bekommst. Das löst du bei einem Parser laut rekursiven Abstieg (da es sich hier gleichzeitig um einen LL(1)-Konflikt handelt (sowohl Deklaration als auch Ausdruck kann mit Identifier anfangen)), indem du einen LL(1)-Konfliktlöser einbaust. Und genau das sagt der Kommentar in der ursprünglich verlinkten ATG.
Um das kurz per vereinfachter Grammatik zu zeigen:
Welche Regel nimmst du nun instatement = ( decl | expr ) ";"; decl = ident { "*" } ident; expr = ident "*" ident;
, wenn dustatement
parsen willst? Du wirst feststellen: es ist völlig egal.a * b;
Aber ist das jetzt eine kontextsensitive Grammatik? Ja, da eine kontextfreie Grammatik auch eine kontextsensitive ist, in diesem Fall wäre das allerdings sogar eine reguläre Sprache (ich nehme jetzt an dieser Stelle trotzdem einfach mal an, dass du zumindest die Beziehung der Klassen zu einander kennst, und die Bedeutung des Unterschiedes zwischen »Grammatik« und »Sprache« in meiner Wortwahl)
Wie man zeigen könnte, dass es sich hier wirklich um eine reguläre Sprache handelt? Grammatik Umformulieren:
Genau das könntest du auch mit der Grammatik für C tun, und du würdest feststellen: kontextfrei, aber nicht hilfreich für einen Parser, der dein Programm parsen soll. Für die formale Eigenschaft »kontextfrei« interessiert aber niemanden, ob man damit Parser bauen könnte. Und somit ist das, was du ursprünglich behauptet hast (»allein durch typedef wird die Sprache C kontextsensitiv«) schlicht falsch. Die Sprache C ist auch ohne typedef kontextsensitiv (wegen typisierten Variablen), und die Sprache der Syntax von C ist kontextfrei, unabhängig von typedef.statement = ident { "*" } ident ";";
Ich sag dir was (zum wiederholten male): Wenn du mir beweisen willst, dass C auch ohne typedef nach der Definition von "kontextfrei" wie in der theoretischen Informatik nicht kontextfrei ist, musst du dir keine Mühe machen. Das weiß ich auch so.
Aber hier ging es nie um dieses Verständnis von "kontextfrei". Denn fast überhaupt keine Programmiersprache (komm jetzt nicht mit Brainfuck) ist in diesem Sinne kontextfrei, selbst wenn sie keine typisierten Variablen haben.
Hier ging es um das Verständnis von "kontextfrei", das Compilerbauer haben. Wenn du nur ein Minimum an common-sense hättest, sollte dir schon durch das Threadthema klar geworden sein, dass hier abgehobene und uferlose Theoretische-Informatik-Debatten (und dazu sinnlos, da eigentlich einfach zu beantworten: C und C++ sind – wie fast jede Programmiersprache – nicht im Sinne der theoretischen Informatik kontextfrei. Ende) 100% fehl am Platze sind.
"Kontextfrei" bedeutet für den Compilerbauer (und für jeden, der sich für die wirklichen Unterschiede zwischen Programmiersprachen interessiert), dass sich Syntax- und Semantik-Überprüfung sauber, einfach und sinnvoll voneinander trennen lassen. Da macht einem bei C das typedef einen Strich durch die Rechnung, abgesehen davon wäre C (in diesem Sinne) kontextfrei. C++ dagegen hat nicht nur eine solche problematische Stelle, sondern ist generell Lichtjahre von Kontextfreiheit entfernt.
Da du hier auf so viele Wikipedia-Artikel verlinkt hast, mache ich das auch mal (und der Artikel ist sogar on-topic!): The lexer hack
Genau um dieses Verständnis von "kontextfrei" wie in diesem Artikel geht es. Nix theoretische Informatik. Klar?Namen im Scanner auflösen oder Konfliktresolver wie bei der verlinkten ATG sind übrigens nicht die einzigen Lösungsmöglichkeiten: du könntest auch eine Regel »typedefdecl-or-mul« o.ä. machen, dann kannst du das im Semantikanschluss prüfen. Ergebnis: gar kein Konflikt mehr in der Grammatik.
Ja, das ist eine hinreichend groteske "Lösungsmöglichkeit". Hinter "typedefdecl-or-mul" kann sich nämlich fast alles verstecken. Komplexe Ausdrücke, die die halbe Grammatik von C "in Anspruch nehmen", wie
. Wenn du so ein Teil dem Semantik-Check übergibst, dann kannst du es eigentlich gleich bleiben lassen.a * ((int) b(x + y, z))
Das passiert schon die ganze Zeit. Nur bist du offensichtlich nicht in der Lage, den Ausführungen bis zu Ende zu folgen und sie auch zu verstehen. Du solltest es auch endlich unterlassen, ständig von Dingen zu reden, von denen du offensichtlich keine Ahnung hast,
Dunning-Kruger?
Beitrag zuletzt geändert: 5.5.2016 12:44:21 von kamakura -
davidlw schrieb:
Wenn es nur darum geht, programmieren zu lernen, sollte man meiner Meinung nach weder C noch C++ lernen. Für den Alltagsgebrauch sind Sprachen wie Python einfach praktischer.
Totaler Blödsinn, wenn du mich fragst. Python nimmt einem die Dinge ab, welche eben grade wichtig sind, wenn man programmieren will. Dazu kommen vollkommen schwachsinnige/unintuitive Operator-Überladungen. Python ist so ziemlich das schlimmste, was je geschaffen wurde. Es ist der Green-Lantern-Movie unter den Programmiersprachen, lediglich so häufig vertreten, weil es so viele schwachsinnige gibt, welche den blödsinn auch noch hypen. Ich meine: Warum ista = [1, 2, 3, 4] b = a a = a + [5]
aberb=[1, 2, 3, 4]
a = [1, 2, 3, 4] b = a a += [5]
?b=[1,2,3,4,5]
Dann kommen semantische Whitespaces... Ich mein: Wenn ich semantische Whitespaces will, lern ich Whitespace, aber doch nicht so ein "Mal so, mal so"-Ding wie Python...
Aus eigener Erfahrung kann ich sagen, dass die Grundprinzipien, die man mit den "urtümlichen" Sprachen lernt und das logische Verständnis, was man dadurch erhält, sich sehr gut auf andere Sprachen übertragen läßt.
Halte ich für Blödsinn. Es ist ein deutlicher Unterschied zwischen funktionalen, imperativen und objektorientierten Sprachen. Der Unterschied geht so tief, dass viele, welche mit funktionalen/imperativen Sprachen beginnen, sich am Ende deutlich schwerer tun, objektorientierte Sprachen zu lernen. Umgekehrt liefern mehr Abstraktionsebenen immer weniger Performance. Ich für meinen Teil würde - wenn ich mich heute entscheiden müsste - mit einer objektorientierten Sprache beginnen und mich dann - je nach bedarf - zurück arbeiten.
Objektorientierung impliziert alles andere irgendwo, aber es vermittelt halt ein Verständnis für das, was heute (und im Beruf) wichtig ist: Abstraktion. Bei aktuellen Projekten ist Hardwarenähe meist gar nicht mehr sooo wichtig und kann ggf. fast immer relativ einfach durch entsprechende Sprachbindungen implementiert werden.
Der Unterschied "C" oder "c++" besteht also denke ich weniger in der Semantik, als viel mehr in der Struktur der daraus entstehenden Programme. Und das ist es, was Leute von Anfang an lernen sollten - denn später wird das schwierig.
Meiner Meinung nach sollten Leute also mit einer rein Objektorientierten Sprache wie beispielsweise Java beginnen. Man kann damit am Ende wenig sinnvolles machen, aber man beginnt mit einer guten Grundlage in Sachen Objektorientierung, hat eine C-ähnliche Syntax und kann dann damit fortsetzen, dinge wie die Garbage-Collection auszuhebeln (C++) schnelle, imperative (C) Programme einzubinden oder ganz hardwarenah zu arbeiten (ASM)
Je tiefer man in die Materie will, desto tiefer geht man. Aber man hat ein ordentliches "Grundgerüst" erlernt, welches gemeinsames Arbeiten ermöglicht. -
kamakura schrieb:
Deine Argumente funktionieren gerade irgendwie so:
"Kontextfrei" bedeutet für den Compilerbauer (und für jeden, der sich für die wirklichen Unterschiede zwischen Programmiersprachen interessiert), dass sich Syntax- und Semantik-Überprüfung sauber, einfach und sinnvoll voneinander trennen lassen. Da macht einem bei C das typedef einen Strich durch die Rechnung, abgesehen davon wäre C (in diesem Sinne) kontextfrei. C++ dagegen hat nicht nur eine solche problematische Stelle, sondern ist generell Lichtjahre von Kontextfreiheit entfernt.A: »und wie man sieht, ist selbst 2 + 3 eine große Zahl, nämlich 6«
Nun gut, wie dem auch sei, ich habe mir nun einmal erlaubt, entsprechende Compilerbauer zu dem Thema zu befragen. Ergebnis:
B: »keine Ahnung wie du da drauf kommst, aber 2 + 3 = 5 …«
A: »Sag mal gehts noch? Ich rechne doch in ℂ, das sollte doch klar sein!«
B: »Und? Ist immer noch 5«
A: »ich red ja auch nicht von der mathematischen Definition mit + = Addition, sondern von der Multiplikation, wie übrigens jeder andere auch, der sich für Zahlen interessiert«Die Syntax [von C] ist parsebar ohne Kontext, da macht das typedef keinen Unterschied.
Ein »Compilerbauer«, der, so wie du es darstellst, auf die formalen Grundlagen scheißt, und nur anhand von »ich glaub aber, dass es so ist« entscheidet, ist im Übrigen nicht mehr als ein Pfuscher/Bastler.
Zu C++: da bekommst du lediglich durch Konstruktoren ein Problem (läuft auf die Frage hinaus, ob es sich um einen Typ handelt, und ob somit
da stehen darf). Jeder »Compilerbauer« wird das aber so lösen, dass die Syntax (für den Parser) dennoch eine CFG ist (Grund: CFGs sind gut erforscht und verstanden, und es gibt gute Algorithmen dafür, alles darüber ist problematisch), und dann im Semantikanschluss etwaige »Syntaxfehler« erkennen und ausgeben.…()
noxious schrieb:
davidlw schrieb, »was man im Alltag brauchen kann«. Und welche Dinge nimmt dir Python überhaupt ab, die wichtig sind, wenn man programmieren will? Effiziente Listen? Strings? Speicherverwaltung? OO? Große Standard-Bibliothek? Paketverwaltung für Bibliotheken? Es hat wohl einen einfachen Grund, warum Sprachen wie Python große Verbreitung haben. Stell dir vor, du hättest ein Problem, bei dem du u.A. Nägel irgendwo einschlagen musst. Der Python-Programmierer sagt: gut, »from werkzeugbox import Hammer; Hammer.einschlagen(Nagel, Wand)«. Und schon kann er sich dem nächsten Problem widmen. Du hingegen stellst erst einmal eine Suche an, ob es denn schon eine fertige Implementierung gibt. Dann stellst du fest: ja, gibts, kann aber genau mit deinen Nägeln nicht umgehen. Also nutzt du die Werkzeugbox, und erweiterst den Gummihammer mit einem Metallkopf zu einem Hammer, sodass er am Ende auch deine Nägel einschlagen kann. Und dann kannst du erst mal genau den einen Nagel einschlagen, für den du das brauchtest. Unterschied? In der Zeit hat der Python-Programmierer schon das nächste Problem gelöst. Vielleicht braucht sein Hammer zwar zum Einschlagen des Nagels 2min, während deiner das in 30s schafft, aber da am Tag sowieso nur ein Nagel eingeschlagen werden muss: egal. Es kommt bei vielen Dingen »im Alltag« nicht darauf an, ob das die effizienteste Lösung ist, aber sehr wohl, ob sie einfach ist, wenige Fehler hat, und schnell entwickelt werden kann. Du musst dir auch keine Gedanken darüber machen, wie eine Liste funktioniert, du willst einfach eine Liste nutzen. Was interessiert mich, wie ich den Speicher verwalten muss, das soll das Ding gefälligst selbst machen. Speicherüberläufe o.ä.? Dann soll es doch mit einem Fehler abstürzen, aber nicht Mist weiter fabrizieren. Ich will mich doch nicht mit der Sprache als Selbstzweck beschäftigen, sondern die soll mir helfen, mein Problem (aus der echten Welt) zu lösen. Und genau das dürfte der Grund sein, warum Sprachen wie Python weit verbreitet sind, wenn es um Algorithmen geht (Algorithmus in Python hast du gleich mal fertig, während du dich in Sprachen wie C/C++/Java/… erst mal um Typen, Wrapper-Objekte, und sonstigen Mist kümmern musst, und dabei willst du sowieso nur wissen, ob der Algorithmus so überhaupt funktioniert), JavaScript, PHP und Ruby, wenn es um Web-Dinge geht usw. Niemand will sich da freiwillig etwas so umständliches wie C(++) antun, wenn es nicht sein muss.
Totaler Blödsinn, wenn du mich fragst. Python nimmt einem die Dinge ab, welche eben grade wichtig sind, wenn man programmieren will.
Dazu kommen vollkommen schwachsinnige/unintuitive Operator-Überladungen. Python ist so ziemlich das schlimmste, was je geschaffen wurde. Es ist der Green-Lantern-Movie unter den Programmiersprachen, lediglich so häufig vertreten, weil es so viele schwachsinnige gibt, welche den blödsinn auch noch hypen. Ich meine: Warum ist
Wie lautet dein Vorschlag für ein besseres Overloading der Operatoren, sodass es sowohl konsistent, als auch verständlich ist? Zur Erinnerung:a = [1, 2, 3, 4] b = a a = a + [5]
aberb=[1, 2, 3, 4]
a = [1, 2, 3, 4] b = a a += [5]
?b=[1,2,3,4,5]
erzeugt ein neues Objekt als Ergebnis, währendx + y
das Objektx += y
verändert. Was das bedeutet: beix
wird eine neue Liste erzeugt, und diese wirda = a + [5]
zugewiesen, während das altea
, auf dasa
eine Referenz hat, unberührt bleibt. Beib
hingegen wird das Element zua += [5]
selbst hinzugefügt.a
ist dabei ein Pointer auf das ursprünglicheb
, und deshalb ändert sich aucha
scheinbar. Wenn sich beides gleich verhalten sollte, wie würdest du das machen? Wennb
dabei+
verändern würde, hättest du ein Problem beia
, und wennc = a + [5]
ein neues Objekt erstellen würde, hättest du ein Problem mit+=
(und du würdest die Performance in den iMer befördern).a == b
Den Titel »die schlimmste Sprache überhaupt« kann Python sicher nicht für sich beanspruchen. Da musst du eher in Richtung PHP oder JavaScript schauen
Dann kommen semantische Whitespaces... Ich mein: Wenn ich semantische Whitespaces will, lern ich Whitespace, aber doch nicht so ein "Mal so, mal so"-Ding wie Python...
Und wie oft muss ich mir Quellcode ansehen, der einfach nicht lesbar ist, weil Whitespace nicht konsistent/sinnvoll benutzt wurde? Für Anfänger ist gerade das also ein Vorteil, wenn sowas bestraft wird …
Meiner Meinung nach sollten Leute also mit einer rein Objektorientierten Sprache wie beispielsweise Java beginnen. Man kann damit am Ende wenig sinnvolles machen […]
Nun, wie viel Software glaubst du, gibt es, die in Java programmiert wurde? Und wenn du dir nun ansiehst, welche Zwecke sowas durchschnittlich erfüllt, glaubst du nicht, dass da fast ausschließlich »sinnvolles« dabei ist, wenn man die ersten Gehversuche der Anfänger dabei ignoriert? Nur so als Hinweis: es gibt wenige Sprachen, die es geschafft haben, von der SmartCard bis hin zum Mainframe eingesetzt zu werden, und es gibt auch relativ wenige Sprachen mit wirklich hochperformanten und effizienten JIT und GC-Implementierungen. Und nun überleg mal, welche Sprachen da wohl dazugehören. Java jedenfalls sicher. -
cachefinder schrieb:
Mit C++ kannst du mehr machen, also würde ich das lernen.
LG Cachefinder
Das stimmt schon. Allerdings ist es auch wichtig, zu wissen warum man etwas lernt.
Einfach nur für sich selber oder um bestimmte Erwartungen zu erfüllen.
Ich würde einfach beides lernen. Einfach nur zur Sicherheit.
Mit C++ würde ich anfangen und dann nach und nach tiefer einsteigen. -
onlinevideorecorder schrieb:
Das stimmt schon.
Nein, das stimmt so pauschal ganz und gar nicht.
Letztlich "kann" C nicht weniger als C++, es it nur evtl. mühsamer.
onlinevideorecorder schrieb:
Allerdings ist es auch wichtig, zu wissen warum man etwas lernt.
Richtig. Für Leute die kein spezielles Ziel vor Augen haben, sondern nur mal ganz generell in die Programmierung "reinschnuppern" wollen, empfehle ich wie gesagt beide Sprachen nicht.
onlinevideorecorder schrieb:
Ich würde einfach beides lernen. Einfach nur zur Sicherheit.
Mit C++ würde ich anfangen und dann nach und nach tiefer einsteigen.
Weil man einfach mal eben beides lernt. Weil man mit dem trivialen und simplen C++ einfach mal anfängt und dann so langsam "tiefer" einsteigt. Die fünf Minuten, die das maximal dauert, hat wohl jeder übrig.
Sorry, aber... du bist jawohl des Wahnsinns fetteste Beute.
Was die Welt auf gar keinen Fall braucht ist einen weiteren Programmierer, der einmal kurz ein Buch über C++ überflogen hat und meint er "kann das". -
annihilus schrieb:
Was die Welt auf gar keinen Fall braucht ist einen weiteren Programmierer, der einmal kurz ein Buch über C++ überflogen hat und meint er "kann das".
Ich glaube echt, du machst das mit Absicht.
Weil es nicht deine Meinung ist, verstehst du mich absichtlich falsch, nur um deine Meinung "aufzupumpen".
Beitrag zuletzt geändert: 12.9.2016 17:11:42 von onlinevideorecorder -
Ich weiß nicht an welcher Stelle genau ich dich missverstanden haben soll, aber du darfst mich gerne erleuchten.
-
Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!
lima-city: Gratis werbefreier Webspace für deine eigene Homepage