Powrót do Delphi na przykładzie ulepszania mojego programu

0

Mój cel:

Wychodząc od zbiorów znaków, chcę dojść do składowych języka programowania np. identyfikatorów zmiennych i typów, dalej składni języka itd., w końcu interpretera. Ale tu na forum chodzi mi tylko o napisanie maksymalnie poprawnie programu, który widać, żeby mieć go na wzór. Potem wątek zakończę. Mam zamiar w Delphi napisać interpreter starego języka programowania na Atari, z czasów komputerów 8-bitowych, języka o nazwie Action! Ale przede wszyskim poćwiczyć Delphi i temu to głównie służy.

ACTION! Reference Manual

Moje doświadczenia w C#

Przykład prostego interpretera własnego języka – podstawy

Jak to się zaczęło:

Wątek został rozpoczęty na moim Mikroblogu. To dwa posty oznaczone hashtagiem #universum Wątek się rozrósł i dlatego przenoszę go na forum. Jak dotąd był to dialog z @furious programming za co jestem mu wdzięczny, za jego zaangażowanie w, że tak powiem "odnowę" mojej osoby, jako programisty. Na chwilę obecną jest to poniższy program, gdzie doszedłem do słówka:

inherited

i nie bardzo wiem, gdzie je tu "przypiąć", co nie znaczy, że nie wiem do czego służy np. do wywołania metody odziedziczonej.

{
Zmiany:
Zakresy cyfr i liter ustalane przy pomocy zbiorów
Aplikacja konsolowa
Log zamiast ShowMessage
Teksty jako stałe np. 'cyfra' jako SDigit
Parametry String jako const
_className zamienione na FClassName (pole prywatne TAlphanum)
Nazwy typów z prefiksem T oraz dalej duża litera, pozostałe małe
Nazwy typów String, Char, Integer z dużej litery
Wprowadzone $REGION - y
Parametry z prefiksem A podobnie do nazw typów
nazwy zmiennych, wprawdzie z małej litery, ale pełnym wyrazem
zwalnianie zmiennych przez Free
Blok chroniony try..except dla zminnej niezainicjowanej 'alien'
}
program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Classes;

const
  SUnknown  = '<Alien>';
  SDigit    = '<Digit>';
  SAlpha    = '<Alpha>';
  SAlphanum = '<Alphanum>';

type
  TDigit = class;
  TAlpha = class;

  TAlphanum = class abstract
  private
    FClassName: String;
  public
    function WhatIs(AChar: Char; ALog: TStrings): String; overload; virtual;
    function WhatIs(ATDigit: TDigit; ALog: TStrings): String; overload; virtual;
    function WhatIs(ATAlpha: TAlpha; ALog: TStrings): String; overload; virtual;
    constructor Create(const AClassName: String);
  end;

  TDigit = class(TAlphanum)
  public
    function WhatIs(AChar: Char; ALog: TStrings): String; override;
    function WhatIs(ATDigit: TDigit; ALog: TStrings): String; override;
  end;

  TAlpha = class(TAlphanum)
  public
    function WhatIs(AChar: Char; ALog: TStrings): String; override;
    function WhatIs(ATAlpha: TAlpha; ALog: TStrings): String; override;
  end;

{$REGION 'TAlphanum'}

constructor TAlphanum.Create(const AClassName: String);
begin
  FClassName := AClassName
end;

function TAlphanum.WhatIs(AChar: Char; ALog: TStrings): String;
var
  digit: TDigit;
  alpha: TAlpha;
begin
  ALog.Add('Have been to #1');
  digit := TDigit.Create(SDigit);
  alpha := TAlpha.Create(SAlpha);
  if digit.WhatIs(AChar, ALog) <> SUnknown then
    Result := digit.WhatIs(AChar, ALog)
  else
  if alpha.WhatIs(AChar, ALog) <> SUnknown then
    Result := alpha.WhatIs(AChar, ALog)
  else
    Result := SUnknown;
  digit.Free;
  alpha.Free;
end;

function TAlphanum.WhatIs(ATDigit: TDigit; ALog: TStrings): String;
var
  digit: TDigit;
begin
  digit := TDigit.Create(SDigit);
  Result := digit.WhatIs(ATDigit, ALog);
  digit.Free;
end;

function TAlphanum.WhatIs(ATAlpha: TAlpha; ALog: TStrings): String;
var
  alpha: TAlpha;
begin
  alpha := TAlpha.Create(SAlpha);
  Result := alpha.WhatIs(ATAlpha, ALog);
  alpha.Free;
end;

{$ENDREGION}

{$REGION 'TDigit'}

function TDigit.WhatIs(AChar: Char; ALog: TStrings): String;
begin
  ALog.Add('Have been to #2');
  if AChar in ['0'..'9'] then
    Result := FClassName
  else
    Result := SUnknown;
end;

function TDigit.WhatIs(ATDigit: TDigit; ALog: TStrings): String;
begin
  ALog.Add('Have been to #3');
  Result := ATDigit.FClassName;
end;

{$ENDREGION}

{$REGION 'TAlpha'}

function TAlpha.WhatIs(AChar: Char; ALog: TStrings): String;
begin
  ALog.Add('Have been to #4');
  if AChar in ['A'..'Z'] then
    Result := FClassName
  else
    Result := SUnknown;
end;

function TAlpha.WhatIs(ATAlpha: TAlpha; ALog: TStrings): String;
begin
  ALog.Add('Have been to #5');
  Result := ATAlpha.FClassName;
end;

{$ENDREGION}

var
  log: TStringList;
  alphanum: TAlphanum;
  digit: TDigit;
  alpha: TAlpha;
  alien: Char;
  i: Integer;
begin
  log := TStringList.Create;
  alphanum := TAlphanum .Create(SAlphanum);
  digit := TDigit.Create(SDigit);
  alpha := TAlpha.Create(SAlpha);
  log.Add('Character ''7'' is of type ' + alphanum.WhatIs('7', log));
  log.Add('Character ''A'' is of type ' + alphanum.WhatIs('A', log));
  log.Add('Character ''$'' is of type ' + alphanum.WhatIs('$', log));
  log.Add('Variable ''digit'' is of type ' + alphanum.WhatIs(digit, log));
  log.Add('Variable ''alpha'' is of type ' + alphanum.WhatIs(alpha, log));
  try
    {[Pascal Warning] W1036 Variable 'alien' might not have been initialized}
    log.Add('Variable ''alien'' is of type ' + alphanum.WhatIs(alien, log));
  except
    log.Add('Error: Variable ''alien'' not initialized');
  end;
  for i := 0 to log.Count - 1 do
    WriteLn(log[i]);
  WriteLn;
  Write('Press key Enter');
  ReadLn;
  log.Free;
  alphanum.Free;
  digit.Free;
  alpha.Free;
end.

Co widać na konsoli:

{
Have been to #1
Have been to #2
Have been to #2
Character '7' is of type <Digit>
Have been to #1
Have been to #2
Have been to #4
Have been to #4
Character 'A' is of type <Alpha>
Have been to #1
Have been to #2
Have been to #4
Character '$' is of type <Alien>
Have been to #3
Variable 'digit' is of type <Digit>
Have been to #5
Variable 'alpha' is of type <Alpha>
Have been to #1
Have been to #2
Have been to #4
Variable 'alien' is of type <Alien>

Press key Enter
}
0

Dobrze by było, abyś najpierw napisał o tym co chcesz osiągnąć. Póki co wiadomo tyle, że masz kilka klas opisujących zbiory jakichś tokenów, ale nie wiadomo do czego one służą.

Nie wiem też dlaczego TAlfanum jest klasą bazową dla klas cyfr (TDigit) i liter (TAlpha). Powinno być na odwrót – na zbiór znaków alfanumerycznych składają się litery i cyfry. Tyle że Delphi nie wspiera wielodziedziczenia klas, wiec trzeba do tematu podejść w podobny sposób do bieżącego, tyle że lepiej ten kod zaplanować.

0

Stosowane rozwiązanie to użycie dwóch pól ze wskazaniami:

FAlpha: TAlpha^; 
FDigit: TDigit^;

Mi się to nie podoba, ale chyba tak zrobię.

0

A koniecznie potrzebujesz do tego celu klas i obiektów? Jeśli nie, to spokojnie możesz sobie takie zestawy znaków trzymać w zbiorach – te można dowolnie wypełniać, przeglądać (w tym iterować po nich), a także wykonywać na nich operacje arytmetyczne (np. w celu ich łączenia czy pozyskiwania części wspólnych).

Przykład:

const
  CHARSET_LETTERS = ['A' .. 'Z', 'a' .. 'z'];
  CHARSET_DIGITS  = ['0' .. '9'];
  CHARSET_SYMBOLS = [';', ':', '.', '(', ')' {..}];

const
  CHARSET_ALPHANUMERIC = CHARSET_LETTERS + CHARSET_NUMBERS;

Jeśli koniecznie chcesz mieć do tego klasy i skorzystać z dziedziczenia i z polimorfizmu, to najpierw określ jakie informacje powinna posiadać klasa bazowa, jakie składowe powinny być publiczne, a jakie chronone i ew. wirtualne. Bez tego trudno będzie stworzyć dobre, uniwersalne klasy.

Artur Protasewicz napisał(a):
FAlpha: TAlpha^; 
FDigit: TDigit^;

Takich rzeczy nie rób, bo się zaplączesz. Referencja sama w sobie jest wskaźnikiem, więc tworzysz w ten sposób wskaźnik na wskaźnik, co potem może być trudne do rozszyfrowania (programiści C znają ten ból).

Co prawda w bibliotece standardowej (przynajmniej dla FPC) istnieją takie typy danych jak PPPointer (wskaźnik na wskaźnik na wskaźnik na dane), ale nie wiem czy ktoś tego używa, czy się po prostu jąkał przy pisaniu kodu.

0
furious programming napisał(a):

Jeśli koniecznie chcesz mieć do tego klasy i skorzystać z dziedziczenia i z polimorfizmu, to najpierw określ jakie informacje powinna posiadać klasa bazowa, jakie składowe powinny być publiczne, a jakie chronone i ew. wirtualne. Bez tego trudno będzie stworzyć dobre, uniwersalne klasy.

Jednak obiektowo bym chciał i muszę rzeczywiście to przemyśleć. Może coś mi da to, co teraz jest wpisywane do loga (zmodyfikowałem trochę wpisy). Może warto się przyjrzeć duplikatom w poniższym logu:

{
Have been to #1 [TAlphanum.WhatIs(Char)]
Have been to #2 [TDigit.WhatIs(Char)]
Have been to #2 [TDigit.WhatIs(Char)]
Character '7' is of type <Digit>
Have been to #1 [TAlphanum.WhatIs(Char)]
Have been to #2 [TDigit.WhatIs(Char)]
Have been to #4 [TAlpha.WhatIs(Char)]
Have been to #4 [TAlpha.WhatIs(Char)]
Character 'A' is of type <Alpha>
Have been to #1 [TAlphanum.WhatIs(Char)]
Have been to #2 [TDigit.WhatIs(Char)]
Have been to #4 [TAlpha.WhatIs(Char)]
Character '$' is of type <Alien>
Have been to #3 [TDigit.WhatIs(TDigit)]
Variable 'digit' is of type <Digit>
Have been to #5 [TAlpha.WhatIs(TAlpha)]
Variable 'alpha' is of type <Alpha>
Have been to #1 [TAlphanum.WhatIs(Char)]
Have been to #2 [TDigit.WhatIs(Char)]
Have been to #4 [TAlpha.WhatIs(Char)]
Variable 'alien' is of type <Alien>

Press key Enter
}
0

W takim razie bottom-up – zacznij od określenia wymagań klasy bazowej, a następnie tych dziedziczących. Przy czym nie deklaruj własnego pola z nazwą klasy, bo takie już istnieje w klasie TObject – spróbuj z niego skorzystać.

I koniecznie wyrzuć istniejący kod, bo jest niezgodny nie tyle z SRP, co z całym SOLID.

0

Sporo pozmieniałem. Przyjąłem założenie, że skoro TAlphanum pyta WhatIs to TAlphanum musi znać odpowiedź.
Ciekawy jest wydruk loga poniżej programu.

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Classes;

const
  SUnknown  = '<Alien>';
  SDigit    = '<Digit>';
  SAlpha    = '<Alpha>';
  SAlphanum = '<Alphanum>';

type
  TDigit = class;
  TAlpha = class;

  TAlphanum = class
  private
    FClassName: String;
  public
    function WhatIs(AChar: Char; ALog: TStrings): String; overload;
    function WhatIs(ATDigit: TDigit; ALog: TStrings): String; overload;
    function WhatIs(ATAlpha: TAlpha; ALog: TStrings): String; overload;
    constructor Create(const AClassName: String);
  end;

  TDigit = class(TAlphanum)
  public
    constructor Create(const AClassName: String);
  end;

  TAlpha = class(TAlphanum)
  public
    constructor Create(const AClassName: String);
  end;

{$REGION 'WhatIs'}

function TAlphanum.WhatIs(AChar: Char; ALog: TStrings): String;
begin
  ALog.Add('[TAlphanum.WhatIs(Char)]');
  if AChar in ['0'..'9'] then
    Result := SDigit
  else
  if AChar in ['A'..'Z'] then
    Result := SAlpha
  else
    Result := SUnknown;
end;

function TAlphanum.WhatIs(ATDigit: TDigit; ALog: TStrings): String;
begin
  ALog.Add('TAlphanum.WhatIs(TDigit)');
  Result := FClassName;
end;

function TAlphanum.WhatIs(ATAlpha: TAlpha; ALog: TStrings): String;
begin
  ALog.Add('[TAlphanum.WhatIs(TAlpha)]');
  Result := FClassName;
end;

{$ENDREGION}

{$REGION 'Create'}

constructor TAlphanum.Create(const AClassName: String);
begin
  FClassName := AClassName
end;

constructor TDigit.Create(const AClassName: string);
begin
  inherited Create(AClassName);
end;

constructor TAlpha.Create(const AClassName: string);
begin
  inherited Create(AClassName);
end;

{$ENDREGION}

var
  log: TStringList;
  alphanum: TAlphanum;
  digit: TDigit;
  alpha: TAlpha;
  alien: Char;
  i: Integer;
begin
  log := TStringList.Create;
  alphanum := TAlphanum .Create(SAlphanum);
  digit := TDigit.Create(SDigit);
  alpha := TAlpha.Create(SAlpha);
  log.Add('Character ''7'' is of type ' + alphanum.WhatIs('7', log));
  log.Add('Character ''A'' is of type ' + alphanum.WhatIs('A', log));
  log.Add('Character ''$'' is of type ' + alphanum.WhatIs('$', log));
  log.Add('Variable ''digit'' is of type ' + alphanum.WhatIs(digit, log));
  log.Add('Variable ''alpha'' is of type ' + alphanum.WhatIs(alpha, log));
  try
    {[Pascal Warning] W1036 Variable 'alien' might not have been initialized}
    log.Add('Variable ''alien'' is of type ' + alphanum.WhatIs(alien, log));
  except
    log.Add('Error: Variable ''alien'' not initialized');
  end;
  for i := 0 to log.Count - 1 do
    WriteLn(log[i]);
  WriteLn;
  Write('Press key Enter');
  ReadLn;
  log.Free;
  alphanum.Free;
  digit.Free;
  alpha.Free;
end.

Log:

{
[TAlphanum.WhatIs(Char)]
Character '7' is of type <Digit>
[TAlphanum.WhatIs(Char)]
Character 'A' is of type <Alpha>
[TAlphanum.WhatIs(Char)]
Character '$' is of type <Alien>
TAlphanum.WhatIs(TDigit)
Variable 'digit' is of type <Alphanum>
[TAlphanum.WhatIs(TAlpha)]
Variable 'alpha' is of type <Alphanum>
[TAlphanum.WhatIs(Char)]
Variable 'alien' is of type <Alien>

Press key Enter
}
0
furious programming napisał(a):

W takim razie bottom-up – zacznij od określenia wymagań klasy bazowej, a następnie tych dziedziczących. Przy czym nie deklaruj własnego pola z nazwą klasy, bo takie już istnieje w klasie TObject – spróbuj z niego skorzystać.

I koniecznie wyrzuć istniejący kod, bo jest niezgodny nie tyle z SRP, co z całym SOLID.

Wiem że jest pole ClassName, ale chcę mieć możliwość nadawania nazw z ogonkami w języku polskim, choć odłożyłem to na dalszy plan.

Prawdę mówiąc mam w głębokim poważaniu te wszyskie angielskie skróty i metodologie, przy całym szacunku do ciebie.
Istnieje tylko top-down i bottom-up i akurat top-down uczyłem się ucząc się pascala.

0

To że nie chcesz pisać kodu zgodnego z SOLID, KISS czy DRY również mam w poważaniu, jednak nie stosując się do tych zasad, automatycznie komplikujesz kod, przez co trudniej go zrozumieć (również mnie), a tym bardziej rozwijać. Dlatego sugeruję pisanie kodu w sposób powszechnie uznawany za poprawny.

Artur Protasewicz napisał(a):

Wiem że jest pole ClassName, ale chcę mieć możliwość nadawania nazw z ogonkami w języku polskim […]

Stanowczo odradzam. Mimo wszystko Delphi pozwala na używanie znaków unikodowych w identyfikatorach:

Fundamental Syntactic Elements: Identifiers

Identifiers denote constants, variables, fields, types, properties, procedures, functions, programs, units, libraries, and packages. An identifier can be of any length, but only the first 255 characters are significant. An identifier must begin with an alphabetic character, a Unicode character, or an underscore (_) and cannot contain spaces. Alphanumeric characters, Unicode characters, digits, and underscores are allowed after the first character.

Natomiast jeśli chodzi o klasę TAlphanum, to w ogóle nie powinna ona posiadać metod WhatIs, bo jest to klasa abstrakcyjna i nie powinna operować na instancjach klas z niej dziedziczących. Chyba że parametr będzie tego samego typu, czyli TAlphanum.

0

może się podłączę :P

najpierw ponarzekam

    function WhatIs(AChar: Char; ALog: TStrings): String; overload;
    function WhatIs(ATDigit: TDigit; ALog: TStrings): String; overload;
    function WhatIs(ATAlpha: TAlpha; ALog: TStrings): String; overload;

Nie podobają mi się nazwy pierwszych parametrów. Po pierwsze to T - T przyjęło się jako oznaczenie typu. Po drugie wg mnie dużo lepiej sprawdziła by się jedna (taka sama) nazwa w każdej metodzie.

następnie

  digit := TDigit.Create(SDigit);
  alpha := TAlpha.Create(SAlpha);

to jest też brzydkie, wręcz bym powiedział, że paskudne :p. Po co przekazywać tam te stałe? A jak zrobię TDigit.Create(SAlpha) albo TAlpha.Create(SDigit) to co wtedy? Ba mogę zrobić nawet tak TAlpha.Create('DUPA) i zadziała. Bo kod się nie wykrzaczy ale wyniki będą cokolwiek dziwne a wymuszając takie tworzenie obiektów wystawiasz na zewnątrz "zwracaną wartość", która jednak powinna być dla zewnętrznych klas niedostępna do modyfikacji.

Generalnie zgadzam się z @furious programming, że te klasy są zbędne. Wg mnie zabierasz się trochę od tyłka strony. Rozumiem, że występują tam jakieś tokeny, zmienne, operatory itd. i to takie klasy powinieneś mieć. Chociaż nie do końca jestem przekonany czy to muszą być klasy czy nie wystarczyło by aby były to rekordy (w tym konkretnym przypadku reprezentowania elementu języka)

0

Klasy nawet mogłyby od biedy pozostać, ale na pewno nie w takiej formie. Metody WhatIs są zbędne – powinno się w konstruktorze klas dziedziczących inicjalizować wartość pola z nazwą klasy (to własne pole) i ewentualnie wystawić je publicznie jako właściwość tylko do odczytu. O ile w ogóle ma ona jakikolwiek sens, w co wątpię.

Problem w tym zalążku kodu polega na tym, że każdy znak jest obiektem, a to spora nadmiarowość. Co za tym idzie, każdy token analizowanego kodu źródłowego będzie musiał być listą obiektów, co znacznie skomplikuje implementację oraz rozciągnie w czasie cały proces parsowania/kompilacji kodu źródłowego.

0

@furious programming: Przepraszam za wczoraj, odnośnie linku poniżej, który dziś znalazłem:

SRP – Single Responsibility Principle w praktyce

Na to wygląda, że cały program jest do wyrzucenia, ale to tak po pierwszym przeczytaniu.
Niemniej on działa i jest szybszy od wersji pierwotnej.
Jeszcze dochodzi korzystanie z TObject.ClassName. To wymaga pozmieniania konstruktorów na bezparametrowe.

Uwzględnię jeszcze to, co pisze @abrakadaber i ujednolicę nazwę zmiennej-parametru metod WhatIs.

0

Teraz tak wygląda program.
Wykorzystałem pole TObject.ClassName.
Nadałem polu FClassName typ ShortString, bo TObject.ClassName jest tego typu
i to wynika z fragmentu o nazwach klas po angielsku.
Użyłem jednakowej nazwy parametru funkcji WhatIs tj. AToken
chociaż nie kojarzę tej nazwy z obiektami Delphi a z obiektami w rozumieniu
gramatyki języka np. polskiego, języka np. programu w Action! etc.
Utworzyłem klasę TAlien, aby w ogóle wyeliminować nazewnictwo
własne w stringach.
Wydruk z konsoli jest na końcu.

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Classes;
{
const
  SUnknown  = '<Alien>';
}
type
  TAlien = class; // odpowiednik stałej Unknown
  TDigit = class;
  TAlpha = class;

  TAlphanum = class
  private
    FClassName: ShortString;
  public
    function WhatIs(AToken: Char; ALog: TStrings): ShortString; overload;
    function WhatIs(AToken: TDigit; ALog: TStrings): ShortString; overload;
    function WhatIs(AToken: TAlpha; ALog: TStrings): ShortString; overload;
    constructor Create;
  end;

  TDigit = class(TAlphanum)
  public
    constructor Create;
  end;

  TAlpha = class(TAlphanum)
  public
    constructor Create;
  end;

  TAlien = class
  private
    FClassName: ShortString;
  public
    constructor Create;
  end;

{$REGION 'Create'}

constructor TAlphanum.Create;
begin
  FClassName := TAlphanum.ClassName;
end;

constructor TDigit.Create;
begin
  FClassName := TDigit.ClassName;
end;

constructor TAlpha.Create;
begin
  FClassName := TAlpha.ClassName;
end;

constructor TAlien.Create;
begin
  FClassName := TAlien.ClassName;
end;

{$ENDREGION}

{$REGION 'WhatIs'}

function TAlphanum.WhatIs(AToken: Char; ALog: TStrings): ShortString;
begin
  ALog.Add('[TAlphanum.WhatIs(Char)]');
  if AToken in ['0'..'9'] then
    Result := TDigit.ClassName
  else
  if AToken in ['A'..'Z'] then
    Result := TAlpha.ClassName
  else
    Result := TAlien.ClassName;
end;

function TAlphanum.WhatIs(AToken: TDigit; ALog: TStrings): ShortString;
begin
  ALog.Add('TAlphanum.WhatIs(TDigit)');
  Result := AToken.ClassName;
end;

function TAlphanum.WhatIs(AToken: TAlpha; ALog: TStrings): ShortString;
begin
  ALog.Add('[TAlphanum.WhatIs(TAlpha)]');
  Result := AToken.ClassName;
end;

{$ENDREGION}

var
  log: TStringList;
  alphanum: TAlphanum;
  digit: TDigit;
  alpha: TAlpha;
  alien: Char;
  i: Integer;
begin
  log := TStringList.Create;
  alphanum := TAlphanum .Create;
  digit := TDigit.Create;
  alpha := TAlpha.Create;
  log.Add('Character ''7'' is of type ' + alphanum.WhatIs('7', log));
  log.Add('Character ''A'' is of type ' + alphanum.WhatIs('A', log));
  log.Add('Character ''$'' is of type ' + alphanum.WhatIs('$', log));
  log.Add('Variable ''digit'' is of type ' + alphanum.WhatIs(digit, log));
  log.Add('Variable ''alpha'' is of type ' + alphanum.WhatIs(alpha, log));
  try
    {[Pascal Warning] W1036 Variable 'alien' might not have been initialized}
    log.Add('Variable ''alien'' is of type ' + alphanum.WhatIs(alien, log));
  except
    log.Add('Error: Variable ''alien'' not initialized');
  end;
  for i := 0 to log.Count - 1 do
    WriteLn(log[i]);
  WriteLn;
  Write('Press key Enter');
  ReadLn;
  log.Free;
  alphanum.Free;
  digit.Free;
  alpha.Free;
end.

{
[TAlphanum.WhatIs(Char)]
Character '7' is of type TDigit
[TAlphanum.WhatIs(Char)]
Character 'A' is of type TAlpha
[TAlphanum.WhatIs(Char)]
Character '$' is of type TAlien
TAlphanum.WhatIs(TDigit)
Variable 'digit' is of type TDigit
[TAlphanum.WhatIs(TAlpha)]
Variable 'alpha' is of type TAlpha
[TAlphanum.WhatIs(Char)]
Variable 'alien' is of type TAlien

Press key Enter
}
0

1. Single Responsibility Principle

Pierwszy rzut oka na wszystkie klasy w przypadku pytania (metody) WhatIs sugeruje, że cała odpowiedzialność spoczywa na klasie TAlphanum,

[ 1 ]
niemniej jeśli się wejrzy w spopsób udzielania odpowiedzi to mamy w ciele WhatIs odpowiedzi przez np. TDigit.ClassName, więc odpowiedzialność ostatecznie realizuje tu: TDigit, co wydaje się zgodne z SRP.

Być może źle to rozumiemiem

[ 2 ]
i chodzi o to, żeby metody np. WhatIs(AObject: TDigit) były w klasie TDigit już na pierwszy rzut oka,

ale to tylko komplikuje program, a efekt będzie ten sam,
więc jestem w kropce.

0

#project-yet-completed @furious programming To jest finalna, jak dotąd wersja. Jeśli będziesz miał chwilę i akceptujesz, przy czym ja biorę sobie do serca to co w tym wątku napisałeś i @abrakadaber , no więc jeśli jesteś skłonny to zaakceptować, to postaw haczyk albo punkt przy tym konkretnym poście.

Właściwie to już chyba wszystko, co tu trzeba zrobić. Zwracam uwagę na zmniejszenie ilości metod WhatIs i użycie w jednej z nich parametru TObject.

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Classes;

type
  TAlien = class;
  TDigit = class;
  TAlpha = class;

  TAlphanum = class
  public
    function WhatIs(AToken: Char; ALog: TStrings): ShortString; overload;
    function WhatIs(AToken: TObject; ALog: TStrings): ShortString; overload;
    constructor Create;
  end;

  TDigit = class(TAlphanum)
  public
    constructor Create;
  end;

  TAlpha = class(TAlphanum)
  public
    constructor Create;
  end;

  TAlien = class
  public
    constructor Create;
  end;

{$REGION 'Create'}

constructor TAlphanum.Create;
begin
  inherited Create;
end;

constructor TDigit.Create;
begin
  inherited Create;
end;

constructor TAlpha.Create;
begin
  inherited Create;
end;

constructor TAlien.Create;
begin
  inherited Create;
end;

{$ENDREGION}

{$REGION 'WhatIs'}

function TAlphanum.WhatIs(AToken: Char; ALog: TStrings): ShortString;
begin
  ALog.Add('[TAlphanum.WhatIs(Char)]');
  if AToken in ['0'..'9'] then
    Result := TDigit.ClassName
  else
  if AToken in ['A'..'Z'] then
    Result := TAlpha.ClassName
  else
    Result := TAlien.ClassName;
end;

function TAlphanum.WhatIs(AToken: TObject; ALog: TStrings): ShortString;
begin
  ALog.Add('[TAlphanum.WhatIs(TObject)]');
  Result := AToken.ClassName;
end;

{$ENDREGION}

var
  log: TStringList;
  alphanum: TAlphanum;
  digit: TDigit;
  alpha: TAlpha;
  alien: Char;
  i: Integer;
begin
  log := TStringList.Create;
  alphanum := TAlphanum .Create;
  digit := TDigit.Create;
  alpha := TAlpha.Create;
  log.Add('Character ''7'' is of type ' + alphanum.WhatIs('7', log));
  log.Add('Character ''A'' is of type ' + alphanum.WhatIs('A', log));
  log.Add('Character ''$'' is of type ' + alphanum.WhatIs('$', log));
  log.Add('Variable ''digit'' is of type ' + alphanum.WhatIs(digit, log));
  log.Add('Variable ''alpha'' is of type ' + alphanum.WhatIs(alpha, log));
  log.Add('Variable ''alphanum'' is of type ' + alphanum.WhatIs(alphanum, log));
  try
    {[Pascal Warning] W1036 Variable 'alien' might not have been initialized}
    log.Add('Variable ''alien'' is of type ' + alphanum.WhatIs(alien, log));
  except
    log.Add('Error: Variable ''alien'' not initialized');
  end;
  for i := 0 to log.Count - 1 do
    WriteLn(log[i]);
  WriteLn;
  Write('Press key Enter');
  ReadLn;
  log.Free;
  alphanum.Free;
  digit.Free;
  alpha.Free;
end.

{
[TAlphanum.WhatIs(Char)]
Character '7' is of type TDigit
[TAlphanum.WhatIs(Char)]
Character 'A' is of type TAlpha
[TAlphanum.WhatIs(Char)]
Character '$' is of type TAlien
[TAlphanum.WhatIs(TObject)]
Variable 'digit' is of type TDigit
[TAlphanum.WhatIs(TObject)]
Variable 'alpha' is of type TAlpha
[TAlphanum.WhatIs(TObject)]
Variable 'alphanum' is of type TAlphanum
[TAlphanum.WhatIs(Char)]
Variable 'alien' is of type TAlien

Press key Enter
}
0
furious programming napisał(a):

Nie wiem też dlaczego TAlfanum jest klasą bazową dla klas cyfr (TDigit) i liter (TAlpha). Powinno być na odwrót – na zbiór znaków alfanumerycznych składają się litery i cyfry. Tyle że Delphi nie wspiera wielodziedziczenia klas, wiec trzeba do tematu podejść w podobny sposób do bieżącego, tyle że lepiej ten kod zaplanować.

Ww. zależność to jest top-down i rzuty, czyli pochodne TAlphanum w kierunkach cyfr i liter. Tak to interpretuję. Zaznaczam, że wolałbym mieć dzidziczenie wielobazowe.

0

Rozmawiam trochę sam ze sobą, ale bardzo liczę na to, że ktoś znacznie lepszy ode mnie wrzuci jeszcze swoje uwagi. Moja reputacja na dziś #75/5027 zniżkująca.

Zrobiłem test użycia przykładowych polskich znaków diakrytycznych w nazwach typów, a dokładnie w jednej - TAlien zamieniłem na TAłień. Na konsoli to trochę kiepsko wychodzi, bo się krzaki wyświetlają zamiast polskich znaków. Oczywiście można powalczyć ze stroną kodową 1250 - Windows, 852 - Polska, itd. ale lepiej liczyć na podsystem I/O, który zrobi wszystko za nas. Zapisałem zamiast na ekranie konsoli w pliku tekstowym i odczytałem w Notatniku (można od razu w edytorze Delphi). Poniżej program z polskimi znakami w nazwie klasy, a na końcu treść pliku. Nadmienię, że jest to zrobione w Turbo Delphi Explorer 2006 (bardzo sympatyczna wersja, jeśli przyjąć że na TFrame robi się komponenty, bo takowych dołączać nie można). Jeśli ktoś koniecznie chce mieć wyświetlone na konsoli to przyda się MODE /?

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Classes;

type
  TAłień = class;
  TDigit = class;
  TAlpha = class;

  TAlphanum = class
  public
    function WhatIs(AToken: Char; ALog: TStrings): ShortString; overload;
    function WhatIs(AToken: TObject; ALog: TStrings): ShortString; overload;
    constructor Create;
  end;

  TDigit = class(TAlphanum)
  public
    constructor Create;
  end;

  TAlpha = class(TAlphanum)
  public
    constructor Create;
  end;

  TAłień = class
  public
    constructor Create;
  end;

{$REGION 'Create'}

constructor TAlphanum.Create;
begin
  inherited Create;
end;

constructor TDigit.Create;
begin
  inherited Create;
end;

constructor TAlpha.Create;
begin
  inherited Create;
end;

constructor TAłień.Create;
begin
  inherited Create;
end;

{$ENDREGION}

{$REGION 'WhatIs'}

function TAlphanum.WhatIs(AToken: Char; ALog: TStrings): ShortString;
begin
  ALog.Add('[TAlphanum.WhatIs(Char)]');
  if AToken in ['0'..'9'] then
    Result := TDigit.ClassName
  else
  if AToken in ['A'..'Z'] then
    Result := TAlpha.ClassName
  else
    Result := TAłień.ClassName;
end;

function TAlphanum.WhatIs(AToken: TObject; ALog: TStrings): ShortString;
begin
  ALog.Add('[TAlphanum.WhatIs(TObject)]');
  Result := AToken.ClassName;
end;

{$ENDREGION}

var
  log: TStringList;
  alphanum: TAlphanum;
  digit: TDigit;
  alpha: TAlpha;
  alien: Char;
  i: Integer;
  f: Text;
begin
  log := TStringList.Create;
  alphanum := TAlphanum .Create;
  digit := TDigit.Create;
  alpha := TAlpha.Create;
  log.Add('Character ''7'' is of type ' + alphanum.WhatIs('7', log));
  log.Add('Character ''A'' is of type ' + alphanum.WhatIs('A', log));
  log.Add('Character ''$'' is of type ' + alphanum.WhatIs('$', log));
  log.Add('Variable ''digit'' is of type ' + alphanum.WhatIs(digit, log));
  log.Add('Variable ''alpha'' is of type ' + alphanum.WhatIs(alpha, log));
  log.Add('Variable ''alphanum'' is of type ' + alphanum.WhatIs(alphanum, log));
  try
    {[Pascal Warning] W1036 Variable 'alien' might not have been initialized}
    log.Add('Variable ''alien'' is of type ' + alphanum.WhatIs(alien, log));
  except
    log.Add('Error: Variable ''alien'' not initialized');
  end;
  AssignFile(f, 'c:\log.txt');
  ReWrite(f);
  for i := 0 to log.Count - 1 do
    WriteLn(f, log[i]);
  WriteLn(f);
  CloseFile(f);
  Write('Press key Enter');
  ReadLn;
  log.Free;
  alphanum.Free;
  digit.Free;
  alpha.Free;
end.

{
[TAlphanum.WhatIs(Char)]
Character '7' is of type TDigit
[TAlphanum.WhatIs(Char)]
Character 'A' is of type TAlpha
[TAlphanum.WhatIs(Char)]
Character '$' is of type TAłień
[TAlphanum.WhatIs(TObject)]
Variable 'digit' is of type TDigit
[TAlphanum.WhatIs(TObject)]
Variable 'alpha' is of type TAlpha
[TAlphanum.WhatIs(TObject)]
Variable 'alphanum' is of type TAlphanum
[TAlphanum.WhatIs(Char)]
Variable 'alien' is of type TAłień

}
0

nie mam zielonego pojęcia co próbujesz osiągnąć zmieniając nazwę typu przez dodanie do niej znaków narodowych

0
abrakadaber napisał(a):

nie mam zielonego pojęcia co próbujesz osiągnąć zmieniając nazwę typu przez dodanie do niej znaków narodowych

Końcowa wersja programu to ta, której post zaczyna się od #project-yet-complted. Dodanie polskich znaków diakrytycznych (narodowych) to tylko test.
Ponieważ usunąłem pole prywatne:

FClassName: string;

wcześniej zwane:

_className: string;

w którym pierwotnie umiszczałem słowa "cyfra", "litera", a więc słowa po polsku, zrobiłem test polskich znaków.
Niemniej to już jest pewne uzupełnienie, a nie wersja finalna.
Tutaj tego nie ma, ale w pewnym momencie myślałem o klasie TCoś, może to mnie skłoniło do testu,
ale generalnie jestem przeciwnikiem pisania programów z polskimi identyfikatorami.

0

Kończę swój udział w wątku i wracam do programowania.
Program pierwotnie, jeszcze na Miroblogu po hashtagiem #universum, został oceniony, jako nienajgorszy.
W tej chwili, w mojej opinii jest dużo lepszy, choć to określenie nieścisłe.

Pozostaje mi praca do zrobienia:
Zapoznanie się z tematyką SRP, SOLID, KISS, DRY, czego nie mogę zrobić na poczekaniu.

Dziękuję za pomoc i udział: @furious programming oraz @abrakadaber

Poniżej wersja końcowa:
(przepraszam za powtórzenie z istniejącego już postu)

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Classes;

type
  TAlien = class;
  TDigit = class;
  TAlpha = class;

  TAlphanum = class
  public
    function WhatIs(AToken: Char; ALog: TStrings): ShortString; overload;
    function WhatIs(AToken: TObject; ALog: TStrings): ShortString; overload;
    constructor Create;
  end;

  TDigit = class(TAlphanum)
  public
    constructor Create;
  end;

  TAlpha = class(TAlphanum)
  public
    constructor Create;
  end;

  TAlien = class
  public
    constructor Create;
  end;

{$REGION 'Create'}

constructor TAlphanum.Create;
begin
  inherited Create;
end;

constructor TDigit.Create;
begin
  inherited Create;
end;

constructor TAlpha.Create;
begin
  inherited Create;
end;

constructor TAlien.Create;
begin
  inherited Create;
end;

{$ENDREGION}

{$REGION 'WhatIs'}

function TAlphanum.WhatIs(AToken: Char; ALog: TStrings): ShortString;
begin
  ALog.Add('[TAlphanum.WhatIs(Char)]');
  if AToken in ['0'..'9'] then
    Result := TDigit.ClassName
  else
  if AToken in ['A'..'Z'] then
    Result := TAlpha.ClassName
  else
    Result := TAlien.ClassName;
end;

function TAlphanum.WhatIs(AToken: TObject; ALog: TStrings): ShortString;
begin
  ALog.Add('[TAlphanum.WhatIs(TObject)]');
  Result := AToken.ClassName;
end;

{$ENDREGION}

var
  log: TStringList;
  alphanum: TAlphanum;
  digit: TDigit;
  alpha: TAlpha;
  alien: Char;
  i: Integer;
begin
  log := TStringList.Create;
  alphanum := TAlphanum .Create;
  digit := TDigit.Create;
  alpha := TAlpha.Create;
  log.Add('Character ''7'' is of type ' + alphanum.WhatIs('7', log));
  log.Add('Character ''A'' is of type ' + alphanum.WhatIs('A', log));
  log.Add('Character ''$'' is of type ' + alphanum.WhatIs('$', log));
  log.Add('Variable ''digit'' is of type ' + alphanum.WhatIs(digit, log));
  log.Add('Variable ''alpha'' is of type ' + alphanum.WhatIs(alpha, log));
  log.Add('Variable ''alphanum'' is of type ' + alphanum.WhatIs(alphanum, log));
  try
    {[Pascal Warning] W1036 Variable 'alien' might not have been initialized}
    log.Add('Variable ''alien'' is of type ' + alphanum.WhatIs(alien, log));
  except
    log.Add('Error: Variable ''alien'' not initialized');
  end;
  for i := 0 to log.Count - 1 do
    WriteLn(log[i]);
  WriteLn;
  Write('Press key Enter');
  ReadLn;
  log.Free;
  alphanum.Free;
  digit.Free;
  alpha.Free;
end.

{
[TAlphanum.WhatIs(Char)]
Character '7' is of type TDigit
[TAlphanum.WhatIs(Char)]
Character 'A' is of type TAlpha
[TAlphanum.WhatIs(Char)]
Character '$' is of type TAlien
[TAlphanum.WhatIs(TObject)]
Variable 'digit' is of type TDigit
[TAlphanum.WhatIs(TObject)]
Variable 'alpha' is of type TAlpha
[TAlphanum.WhatIs(TObject)]
Variable 'alphanum' is of type TAlphanum
[TAlphanum.WhatIs(Char)]
Variable 'alien' is of type TAlien

Press key Enter
}
0

a wytłumacz mi jeszcze po co są Ci te puste konstruktory?

constructor TAlien.Create;
begin
  inherited Create;
end;

tak naprawdę aby to miało ręce i nogi to powinieneś mieć interfejs (bo w delphi nie ma de facto klas abstrakcyjnych ani możliwości na zablokowanie utworzenia instancji klasy), po którym będą dziedziczyły te klasy TAlien, TDigit i TAlpha. Wtedy w metodzie function WhatIs(AToken: TObject; ALog: TStrings): ShortString; overload; jako parametr nie przekazujesz TObject tylko ten interfejs. Zapobiegnie to zrobienia np. czegoś takiego

  alpha: TStringList;
begin
  alpha := TStringList.Create;
  log.Add('Variable ''digit'' is of type ' + alphanum.WhatIs(alpha, log));

BTW ta linijka (i analogiczne) log.Add('Variable ''digit'' is of type ' + alphanum.WhatIs(alpha, log)); jest co najmniej dziwna - najpierw masz log.Add a potem przekazujesz log do metody WhatIs. Ja rozumiem, że to do logowania ale jednak

0

Kolejna wersja. Dla mnie w pełni akceptowalna:

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Classes;

type
  TAlien = class;
  TDigit = class;
  TAlpha = class;

  TAlphanum = class
  public
    function WhatIs(AToken: Char): ShortString; overload;
    function WhatIs(AToken: TObject): ShortString; overload;
    constructor Create;
  end;

  TDigit = class(TAlphanum)
  public
    constructor Create;
  end;

  TAlpha = class(TAlphanum)
  public
    constructor Create;
  end;

  TAlien = class
  public
    constructor Create;
  end;

{$REGION 'Create'}

constructor TAlphanum.Create;
begin
  inherited Create;
end;

constructor TDigit.Create;
begin
  inherited Create;
end;

constructor TAlpha.Create;
begin
  inherited Create;
end;

constructor TAlien.Create;
begin
  inherited Create;
end;

{$ENDREGION}

{$REGION 'WhatIs'}

function TAlphanum.WhatIs(AToken: Char): ShortString;
begin
  if AToken in ['0'..'9'] then
    Result := TDigit.ClassName
  else
  if AToken in ['A'..'Z'] then
    Result := TAlpha.ClassName
  else
    Result := TAlien.ClassName;
end;

function TAlphanum.WhatIs(AToken: TObject): ShortString;
begin
  Result := AToken.ClassName;
end;

{$ENDREGION}

var
  alphanum: TAlphanum;
  digit: TDigit;
  alpha: TAlpha;
  alien: Char;
begin
  alphanum := TAlphanum .Create;
  digit := TDigit.Create;
  alpha := TAlpha.Create;
  WriteLn(alphanum.WhatIs('7'));
  WriteLn(alphanum.WhatIs('A'));
  WriteLn(alphanum.WhatIs('$'));
  WriteLn(alphanum.WhatIs(digit));
  WriteLn(alphanum.WhatIs(alpha));
  WriteLn(alphanum.WhatIs(alphanum));
  try
    {[Pascal Warning] W1036 Variable 'alien' might not have been initialized}
    WriteLn(alphanum.WhatIs(alien));
  except
    WriteLn('Error: Variable not initialized');
  end;
  Write('Press key Enter');
  ReadLn;
  alphanum.Free;
  digit.Free;
  alpha.Free;
end.

{
TDigit
TAlpha
TAlien
TDigit
TAlpha
TAlphanum
TAlien
Press key Enter
}
2

skopiuj, skompiluj i zobacz, że nie ma żadnej różnicy :)

program Project1;
 
{$APPTYPE CONSOLE}
 
uses
  SysUtils,
  Classes;
 
type
   TAlphanum = class
  public
    function WhatIs(AToken: Char): ShortString; overload;
    function WhatIs(AToken: TObject): ShortString; overload;
  end;
 
  TDigit = class(TAlphanum)
  end;
 
  TAlpha = class(TAlphanum)
  end;
 
  TAlien = class  //BTW dlaczego nie dziedziczy po TAlphanum?
  end;

 {$REGION 'WhatIs'}
 
function TAlphanum.WhatIs(AToken: Char): ShortString;
begin
  if AToken in ['0'..'9'] then
    Result := TDigit.ClassName
  else
  if AToken in ['A'..'Z'] then
    Result := TAlpha.ClassName
  else
    Result := TAlien.ClassName;
end;
 
function TAlphanum.WhatIs(AToken: TObject): ShortString;
begin
  Result := AToken.ClassName;
end;
 
{$ENDREGION}
 
var
  alphanum: TAlphanum;
  digit: TDigit;
  alpha: TAlpha;
  alien: Char;
begin
  alphanum := TAlphanum .Create;
  digit := TDigit.Create;
  alpha := TAlpha.Create;
  WriteLn(alphanum.WhatIs('7'));
  WriteLn(alphanum.WhatIs('A'));
  WriteLn(alphanum.WhatIs('$'));
  WriteLn(alphanum.WhatIs(digit));
  WriteLn(alphanum.WhatIs(alpha));
  WriteLn(alphanum.WhatIs(alphanum));
  try
    {[Pascal Warning] W1036 Variable 'alien' might not have been initialized}
    WriteLn(alphanum.WhatIs(alien));
  except
    WriteLn('Error: Variable not initialized');
  end;
  Write('Press key Enter');
  ReadLn;
  alphanum.Free;
  digit.Free;
  alpha.Free;
end.
0

POST FACTUM

Zaczynam rozdział po udoskonaleniu programu Delphi do poziomu, który nazwę wzorcowym dla moich dalszych poczynań programistycznych.
Został postawiony haczyk, jest w kółku.
Ostateczną postać programowi (na tym etapie) nadał @abrakadaber
Obrałem sobie za następne rzeczy do zrobienia: liczbę heksadecymalną i identyfikator.
Zgadzam się z @vpiotr że trzeba mówić raczej o ciągach znaków, czyli o typie String (ew. ShortString) i ten etap teraz się zaczyna.
Myślę, że rozwijanie wątku ma sens, niezależnie od istnienia rozwiązania oznaczonego haczykiem.
Jak sądzicie, kontynuować ten wątek, czy zacząć nowy? @furious programming @abrakadaber @vpiotr

0
Artur Protasewicz napisał(a):

Obrałem sobie za następne rzeczy do zrobienia: liczbę heksadecymalną i identyfikator.
Zgadzam się z @vpiotr że trzeba mówić raczej o ciągach znaków, czyli o typie String (ew. ShortString) i ten etap teraz się zaczyna.

O to właśnie pytałem od samego początku – po co są te klasy. Na początku przypuszczałem, że są one typem Char na sterydach, które do reprezentacji dowolnego ciągu znaków będą wymagać ręcznej alikacji pamięci (konstruktory klas) oraz przechowywania ich w liście (której pamięcią też trzeba będzie samemu zarządzać).

Od początku sądziłem, że to spory overcoding, niewarty czasu, a i solidna komplikacja dość prostych zadań. Nie mam tutaj na myśli analizy kodu źródłowego, a takich czynności jak ekstrakcja fragmentów tekstu czy porównywanie ciągów. Biblioteka standardowa posiada mnóstwo gotowych funkcji i typów danych, a Ty całe to dobrodziejstwo chcesz wyrzucić do kąta i samemu pisać wszystko od podstaw. Bez jakiegokolwiek zysku – ani krócej nie będzie trwało pisanie kodu, ani nie będzie łatwiejsze, ani sam kod nie będzie działał szybciej.

Dlatego nie rozumiem po co to robisz i tracisz czas na rzeczy, których nie potrzebujesz.

Myślę, że rozwijanie wątku ma sens, niezależnie od istnienia rozwiązania oznaczonego haczykiem.
Jak sądzicie, kontynuować ten wątek, czy zacząć nowy?

Przyjęło się na forum, że jeden wątek dotyczy jednego problemu. Rób jak uważasz, byle uniknąć bałaganu.

0
furious programming napisał(a):

Przyjęło się na forum, że jeden wątek dotyczy jednego problemu. Rób jak uważasz, byle uniknąć bałaganu.

Trochę powalczyłem z liczbą hex i widzę, że rzeczywiście zrobi się bałagan, bo już nie widzę rozwiązania bez iteracji na typie String i funkcji Pos(SubStr, Str): Integer i nie wiem jak to "zjeść" modelem, który dotąd omawialiśmy. Tworzy się jakaś dwoistość modelu i to jest bałagan.

Problem był. Został rozwikłany. I na tym koniec. Dzięki za pomoc.

0

Klasami powinieneś opisywać konkretne tokeny i gromadzić je w jakichś kontenerach (generycznych listach czy drzewach – w zależności od wymagań). Natomiast kod powineneś trzymać jako standardowy ciąg znaków i używać funkcji z biblioteki standardowej do jego przetwarzania.

Ok, skoro nie chcesz dalej dyskutować to na tym zakończmy. W razie problemów pytaj śmiało.

0

Jeden z kolegów z tego forum popełnił kompilator języka w Pascalu - @Patryk27. Duża część, jak struktura głów,a parsowanie, optymalizacja etc. będzie podobna, między językami, więc sugeruje najpierw zobaczyć, co i jak się piszę, a nie tworzyć takich "wywijasów" ;) Na uczelni jak ktoś pisał sam niepotrzebne biblioteki, zamiast zrozumieć algorytm i po prostu używać standardowej z danego języka, to jeden wykładowca sugerował, że do liczenia całek też powinniśmy sami dojść, a nie z książek. Świat bez korzystania z dziedzictwa stal by w miejscu, a tak to idziemy do przodu i obecnie nie trzeba np. pisać obsługi listy czy mamy kolekcje bezpieczne pod względem wielu wątków. Polecam zacząć od lektury https://github.com/Patryk27/SScript-Compiler. Zresztą, to też nie jest tak, że w taki sposób ręczny powinno się parsery robić - od tego są narzędzia jak Bison, Yacc, Lex - co prawda można w tym opisać gramatykę i generować parsery w C, ale widzę, że ktoś i dla Delphi napisał geneator na bazie tych tooli - https://github.com/RomanYankovsky/ndyacclex Nader wszystko proponuję zacząć, od teorii https://pl.wikipedia.org/wiki/Generator_parser%C3%B3w No i życzę serdecznie powodzenia i przyjemności podczas zabawy z Delphi na urlopie ;)

0

@somedev: Powtórzę teksty, które są tylko na moim Mikroblogu, a od których ten wątek się zaczyna:

HashTag: #universum

Mikroblog. Post 1.

Czasami popadam w stwarzanie świata od nowa i myślę nad czymś, co już dawno wymyślono. Myślę sobie: diabli nadali tę informatykę, a z drugiej strony - przecież to kochasz i to twój najlepszy azyl. Ale też pojawia się dość ważne pytanie w kontekście korzystania z rzeczy gotowych, już stworzonych, doskonalonych i utrwalonych przez lata - czy ktoś będzie umiał to zrobić od nowa tak samo, za pięć, dziesięć, dwadzieścia lat? Niby po co i dlaczego miałaby istnieć taka potrzeba? Trudno powiedzieć, czy nastąpi jakaś zagłada, którą chętnie posiłkują się media i różne popularno-naukowe filmy. A może świat będzie posługiwał się zupełnie nowymi środkami komunikacji. Za bardzo w to nie wierzę, choć to na pewno subiektywne odczucie - ani w zagładę nie wierzę, ani w nowe środki komunikacji.

Mikroblog. Post 2.

Metodą prób i błędów, nieco popartą wyczuciem wynikającym z doświadczenia, wchodząc w interakcję z kompilatorem Delphi, pociągnąłem to, co zacząłem postem wcześniejszym z tym samym hashtagiem. Przypominam sobie Delphi, wybierając losowo tematy, które są w stanie mnie trochę wciągnąć. Zacznę od tego, co zobaczyłem na końcu swojej ni to pracy, ni to zabawy. Odnotowuję chwilę radości programisty, nawet, jeśli to wszystko jest bez sensu.

0

No to życzę przyjemności w tym kręceniu korbką, bo chyba do tego się to sprowadza ;)

1 użytkowników online, w tym zalogowanych: 0, gości: 1