portaldacalheta.pt
  • Главни
  • Процес И Алати
  • Рисе Оф Ремоте
  • Профитабилност И Ефикасност
  • Инжењерски Менаџмент
Технологија

Рода, 4. део: Примена изјава и завршавање



У нашој потрази за стварањем лаганог програмског језика помоћу Ц ++, започели смо са израдом нашег токенизера пре три недеље, а затим смо у следеће две недеље применили процену израза.

Сада је време да се заврши и испоручи комплетан програмски језик који неће бити толико моћан као зрео програмски језик, али ће имати све потребне карактеристике, укључујући врло мали отисак.



Смијешно ми је како нове компаније имају одјељке с честим питањима на својим веб локацијама који не одговарају на питања која се често постављају, већ на питања која желе да им се поставе. Ја ћу то учинити и овде. Људи који прате мој рад често ме питају зашто се Сторк не преведе у неки бајт код или бар у неки средњи језик.



Зашто се Сторк не компајлира у бајткод?

Радо ћу одговорити на ово питање. Циљ ми је био да развијем скриптни језик малог отиска који ће се лако интегрисати са Ц ++. Немам строгу дефиницију „отиска мале величине“, али замишљам компајлер који ће бити довољно мали да омогући преносљивост на мање моћне уређаје и неће трошити превише меморије приликом покретања.



Ц ++ Сторк

Нисам се фокусирао на брзину, јер мислим да ћете кодирати на Ц ++ ако имате временски важан задатак, али ако вам је потребна нека врста проширивости, језик попут Сторк могао би бити користан.



Не тврдим да не постоје други, бољи језици који могу да изврше сличан задатак (на пример, Луа ). Било би заиста трагично да нису постојали, а ја вам само дајем идеју о случају употребе овог језика.

Будући да ће бити уграђен у Ц ++, сматрам да је корисно користити неке постојеће карактеристике Ц ++-а уместо да напишем читав екосистем који ће служити у сличну сврху. Не само то, већ ми је и овај приступ занимљивији.



Као и увек, пуни изворни код можете пронаћи на мојој ГитХуб страница . Погледајмо сада ближе наш напредак.

Промене

До овог дела, Сторк је био делимично комплетан производ, тако да нисам успео да видим све његове недостатке и недостатке. Међутим, како је попримио потпунији облик, променио сам следеће ствари уведене у претходним деловима:



  • Функције више нису променљиве. Постоји засебан function_lookup у compiler_context Сада. function_param_lookup је преименован у param_lookup да би се избегла забуна.
  • Променио сам начин на који се позивају функције. Постоји call метода у runtime_context то траје std::vector аргумената, чува стари индекс повратне вриједности, гура аргументе у стек, мијења индекс повратне вриједности, позива функцију, искаче аргументе из стека, враћа стари индекс повратне вриједности и враћа резултат. На тај начин, не морамо да држимо стог индекса повратне вредности, као раније, јер стек Ц ++ служи у ту сврху.
  • РАИИ класе додате у compiler_context који се враћају позивима функцијама чланова scope и function. Сваки од тих објеката ствара нове local_identifier_lookup и param_identifier_lookup, у њиховим конструкторима и враћају старо стање у деструктор.
  • РАИИ класа додата у runtime_context, коју враћа функција члана get_scope. Та функција складишти величину стека у свом конструктору и враћа је у деструктор.
  • Уклонио сам const кључна реч и константни објекти уопште. Могли би бити корисни, али нису апсолутно неопходни.
  • var кључна реч уклоњена, јер тренутно уопште није потребна.
  • Додао сам sizeof кључна реч која ће током извршавања проверити величину низа. Можда ће неки програмери за Ц ++ сматрати да је избор имена богохулно, као Ц ++ sizeof ради у времену компајлирања, али изабрао сам ту кључну реч како бих избегао колизију са неким уобичајеним именом променљиве - на пример, size.
  • Додао сам tostring кључна реч која експлицитно претвара било шта у string. То не може бити функција, јер не дозвољавамо преоптерећење функције.
  • Разне мање занимљиве промене.

Синтакса

Пошто користимо синтаксу врло сличну Ц-у и сродним програмским језицима, даћу вам само детаље који можда нису јасни.

Декларације променљивих су следеће:



  • void, користи се само за тип повратка функције
  • number
  • string
  • T[] је низ онога што садржи елементе типа T
  • R(P1,...,Pn) је функција која враћа тип R и прима аргументе типова P1 до Pn. Сваком од тих типова може се додати префикс & ако се преноси референцом.

Декларација функције је следећа: [public] function R name(P1 p1, … Pn pn)

пример атрибута објекта може бити

Дакле, мора имати префикс function. Ако је са префиксом public, онда се може позвати са Ц ++. Ако функција не врати вредност, процениће према подразумеваној вредности свог типа повратка.



Дозвољавамо for -петљу са декларацијом у првом изразу. Такође дозвољавамо if -изјаву и switch -изјаву са изразом иницијализације, као у Ц ++ 17. Изјава if започиње са if -блоком, након чега следи нула или више elif -блокова, а по жељи и један else -блок. Ако је променљива декларисана у изразу иницијализације изјаве if, била би видљива у сваком од тих блокова.

Дозвољавамо необавезни број после break изјава која се може одвојити од више угнежђених петљи. Дакле, можете имати следећи код:

for (number i = 0; i <100; ++i) { for(number j = 0; j < 100; ++j) { if (rnd(100) == 0) { break 2; } } }

Такође, прекинуће се са обе петље. Тај број се потврђује током времена компајлирања. Како је то кул?

Састављач

Многе функције су додате у овај део, али ако будем превише детаљан, вероватно ћу изгубити и најупорније читаоце који ме и даље носе. Због тога ћу намерно прескочити један врло велики део приче - компилацију.

То је зато што сам то већ описао у први и друго делови ове серије блогова. Фокусирао сам се на изразе, али састављање било чега другог није много другачије.

Даћу вам, међутим, један пример. Овај код компајлира while изјаве:

statement_ptr compile_while_statement( compiler_context& ctx, tokens_iterator& it, possible_flow pf ) { parse_token_value(ctx, it, reserved_token::kw_while); parse_token_value(ctx, it, reserved_token::open_round); expression::ptr expr = build_number_expression(ctx, it); parse_token_value(ctx, it, reserved_token::close_round); block_statement_ptr block = compile_block_statement(ctx, it, pf); return create_while_statement(std::move(expr), std::move(block)); }

Као што видите, далеко је од тога да је сложено. Анализира while, затим (, затим гради бројчани израз (немамо логичке вредности), а затим рашчлањује ).

Након тога саставља блок наредбу која се може налазити унутар { и } или не (да, дозволио сам блокове са једним исказом) и ствара while изјава на крају.

Већ су вам позната прва два аргумента функције. Трећа, possible_flow, приказује дозвољене наредбе за промену протока (continue, break, return) у контексту који рашчлањујемо. Могао бих да чувам те информације у објекту да су изрази компилације функције члана неких compiler класе, али нисам велики љубитељ мамутских класа, а преводилац би дефинитивно био једна таква класа. Доношење додатног аргумента, посебно танког, неће никога повредити, а ко зна, можда једног дана успемо да паралелизујемо код.

Постоји још један занимљив аспект компилације који бих желео овде да објасним.

Ако желимо да подржимо сценарио у којем се две функције међусобно позивају, то можемо учинити на начин Ц: дозвољавањем прослеђивања декларације или две фазе компајлирања.

Изабрао сам други приступ. Када се пронађе дефиниција функције, рашчланит ћемо њен тип и име у објект с именом incomplete_function. Тада ћемо прескочити његово тело, без тумачења, једноставним бројањем нивоа гнежђења коврџавих заграда док не затворимо прву коврџаву заграду. У процесу ћемо прикупљати токене, задржати их у incomplete_function и додати идентификатор функције у compiler_context.

Једном када прођемо целу датотеку, компајлираћемо сваку од функција у потпуности, тако да се оне могу позивати током извршавања. На тај начин свака функција може позвати било коју другу функцију у датотеци и може приступити било којој глобалној променљивој.

Глобалне променљиве могу се иницијализовати позивима на исте функције, што нас одмах доводи до старог проблема „пилетина и јаја“ чим те функције приступе неиницијализованим променљивим.

Ако се то икада догоди, проблем се решава додавањем runtime_exception - и то само зато што сам фин. Франки, кршење приступа је најмање што можеш добити као казну за писање таквог кода.

Глобални опсег

Постоје две врсте ентитета који се могу појавити у глобалном опсегу:

  • Глобалне променљиве
  • Функције

Свака глобална променљива може се иницијализовати изразом који враћа тачан тип. Иницијализатор се креира за сваку глобалну променљиву.

Сваки иницијализатор враћа lvalue, тако да они служе као конструктори глобалних променљивих. Када није дат израз за глобалну променљиву, конструише се подразумевани иницијализатор.

Ово је initialize функција члана у runtime_context:

void runtime_context::initialize() { _globals.clear(); for (const auto& initializer : _initializers) { _globals.emplace_back(initializer->evaluate(*this)); } }

Позива се из конструктора. Брише контејнер глобалне променљиве, како се то може експлицитно позвати, да ресетује runtime_context стање.

Као што сам раније поменуо, морамо да проверимо да ли приступамо неиницијализованој глобалној променљивој. Стога је ово глобални приступник променљивим:

variable_ptr& runtime_context::global(int idx) { runtime_assertion( idx <_globals.size(), 'Uninitialized global variable access' ); return _globals[idx]; }

Ако први аргумент има вредност false, runtime_assertion баца а runtime_error са одговарајућом поруком.

Свака функција је имплементирана као ламбда која снима појединачни израз, који се затим вреднује са runtime_context које функција прима.

Обим функције

Као што сте могли видети из while -компајлације изјаве, преводилац се позива рекурзивно, почев од наредбе блок, која представља блок целокупне функције.

Ево апстрактне основне класе за све изјаве:

class statement { statement(const statement&) = delete; void operator=(const statement&) = delete; protected: statement() = default; public: virtual flow execute(runtime_context& context) = 0; virtual ~statement() = default; };

Једина функција осим заданих је execute, која изводи логику израза на runtime_context и враћа flow, који одређује куда ће програмска логика ићи даље.

enum struct flow_type{ f_normal, f_break, f_continue, f_return, }; class flow { private: flow_type _type; int _break_level; flow(flow_type type, int break_level); public: flow_type type() const; int break_level() const; static flow normal_flow(); static flow break_flow(int break_level); static flow continue_flow(); static flow return_flow(); flow consume_break(); };

Статичке функције креатора су саме по себи разумљиве и написао сам их да бих спречио нелогичност flow са нулама break_level и тип који се разликује од flow_type::f_break.

Сада, consume_break ће створити прекидни проток са једним мање нивоом прекида или, ако ниво прекида достигне нулу, нормалним протоком.

Сада ћемо проверити све типове изјава:

class simple_statement: public statement { private: expression::ptr _expr; public: simple_statement(expression::ptr expr): _expr(std::move(expr)) { } flow execute(runtime_context& context) override { _expr->evaluate(context); return flow::normal_flow(); } };

Овде, simple_statement је исказ који је створен од израза. Сваки израз се може компајлирати као израз који враћа void, тако да simple_statement може се створити од ње. Као ни break нити continue или return може бити део израза, simple_statement враћа flow::normal_flow().

class block_statement: public statement { private: std::vector _statements; public: block_statement(std::vector statements): _statements(std::move(statements)) { } flow execute(runtime_context& context) override { auto _ = context.enter_scope(); for (const statement_ptr& statement : _statements) { if ( flow f = statement->execute(context); f.type() != flow_type::f_normal ){ return f; } } return flow::normal_flow(); } };

Тхе block_statement задржава std::vector изјава. Погубљује их, једног по једног. Ако сваки од њих врати не-нормалан проток, одмах враћа тај проток. Користи РАИИ објект опсега да дозволи декларације променљивих локалног опсега.

class local_declaration_statement: public statement { private: std::vector _decls; public: local_declaration_statement(std::vector decls): _decls(std::move(decls)) { } flow execute(runtime_context& context) override { for (const expression::ptr& decl : _decls) { context.push(decl->evaluate(context)); } return flow::normal_flow(); } };

local_declaration_statement процењује израз који ствара локалну променљиву и гура нову локалну променљиву у стек.

class break_statement: public statement { private: int _break_level; public: break_statement(int break_level): _break_level(break_level) { } flow execute(runtime_context&) override { return flow::break_flow(_break_level); } };

break_statement има ниво прекида процењен током времена компајлирања. Само враћа ток који одговара том нивоу прекида.

class continue_statement: public statement { public: continue_statement() = default; flow execute(runtime_context&) override { return flow::continue_flow(); } };

continue_statement само враћа flow::continue_flow().

class return_statement: public statement { private: expression::ptr _expr; public: return_statement(expression::ptr expr) : _expr(std::move(expr)) { } flow execute(runtime_context& context) override { context.retval() = _expr->evaluate(context); return flow::return_flow(); } }; class return_void_statement: public statement { public: return_void_statement() = default; flow execute(runtime_context&) override { return flow::return_flow(); } };

return_statement и return_void_statement оба враћају flow::return_flow(). Једина разлика је у томе што први има израз који обрачунава на повратну вредност пре него што се врати.

за шта се користи скала
class if_statement: public statement { private: std::vector _exprs; std::vector _statements; public: if_statement( std::vector exprs, std::vector statements ): _exprs(std::move(exprs)), _statements(std::move(statements)) { } flow execute(runtime_context& context) override { for (size_t i = 0; i evaluate(context)) { return _statements[i]->execute(context); } } return _statements.back()->execute(context); } }; class if_declare_statement: public if_statement { private: std::vector _decls; public: if_declare_statement( std::vector decls, std::vector exprs, std::vector statements ): if_statement(std::move(exprs), std::move(statements)), _decls(std::move(decls)) { } flow execute(runtime_context& context) override { auto _ = context.enter_scope(); for (const expression::ptr& decl : _decls) { context.push(decl->evaluate(context)); } return if_statement::execute(context); } };

if_statement, која се креира за један if -блок, нула или више elif -блокова и један else -блок (који може бити празан), процењује сваки од његових изрази док један израз не добије вредност 1. Затим извршава тај блок и враћа резултат извршења. Ако ниједан израз не израчуна 1, вратит ће извршење задњег (else) блока.

if_declare_statement је изјава која има декларације као први део иф-клаузуле. Гурне све декларисане променљиве у стек, а затим извршава своју основну класу (if_statement).

class switch_statement: public statement { private: expression::ptr _expr; std::vector _statements; std::unordered_map _cases; size_t _dflt; public: switch_statement( expression::ptr expr, std::vector statements, std::unordered_map cases, size_t dflt ): _expr(std::move(expr)), _statements(std::move(statements)), _cases(std::move(cases)), _dflt(dflt) { } flow execute(runtime_context& context) override { auto it = _cases.find(_expr->evaluate(context)); for ( size_t idx = (it == _cases.end() ? _dflt : it->second); idx execute(context); f.type()) { case flow_type::f_normal: break; case flow_type::f_break: return f.consume_break(); default: return f; } } return flow::normal_flow(); } }; class switch_declare_statement: public switch_statement { private: std::vector _decls; public: switch_declare_statement( std::vector decls, expression::ptr expr, std::vector statements, std::unordered_map cases, size_t dflt ): _decls(std::move(decls)), switch_statement(std::move(expr), std::move(statements), std::move(cases), dflt) { } flow execute(runtime_context& context) override { auto _ = context.enter_scope(); for (const expression::ptr& decl : _decls) { context.push(decl->evaluate(context)); } return switch_statement::execute(context); } };

switch_statement извршава своје исказе један по један, али прво прелази на одговарајући индекс који добија из процене израза. Ако било која од његових наредби врати не-нормалан проток, одмах ће вратити тај ток. Ако има flow_type::f_break, прво ће потрошити један прекид.

switch_declare_statement дозвољава декларацију у заглављу. Ниједна од њих не дозвољава декларацију у телу.

class while_statement: public statement { private: expression::ptr _expr; statement_ptr _statement; public: while_statement(expression::ptr expr, statement_ptr statement): _expr(std::move(expr)), _statement(std::move(statement)) { } flow execute(runtime_context& context) override { while (_expr->evaluate(context)) { switch (flow f = _statement->execute(context); f.type()) { case flow_type::f_normal: case flow_type::f_continue: break; case flow_type::f_break: return f.consume_break(); case flow_type::f_return: return f; } } return flow::normal_flow(); } }; class do_statement: public statement { private: expression::ptr _expr; statement_ptr _statement; public: do_statement(expression::ptr expr, statement_ptr statement): _expr(std::move(expr)), _statement(std::move(statement)) { } flow execute(runtime_context& context) override { do { switch (flow f = _statement->execute(context); f.type()) { case flow_type::f_normal: case flow_type::f_continue: break; case flow_type::f_break: return f.consume_break(); case flow_type::f_return: return f; } } while (_expr->evaluate(context)); return flow::normal_flow(); } };

while_statement и do_while_statement обојица извршавају свој боди израз док њихов израз процењује на 1. Ако се извршење врати flow_type::f_break, они је потроше и врате. Ако се врати flow_type::f_return, они је врате. У случају нормалног извршења или наставка, они не раде ништа.

Може изгледати као да continue нема ефекта. Међутим, то је утицало на унутрашњу изјаву. Ако је, на пример, block_statement, није проценило до краја.

Сматрам да је то уредно while_statement је имплементиран са Ц ++ while и do-statement са Ц ++ do-while.

class for_statement_base: public statement { private: expression::ptr _expr2; expression::ptr _expr3; statement_ptr _statement; public: for_statement_base( expression::ptr expr2, expression::ptr expr3, statement_ptr statement ): _expr2(std::move(expr2)), _expr3(std::move(expr3)), _statement(std::move(statement)) { } flow execute(runtime_context& context) override { for (; _expr2->evaluate(context); _expr3->evaluate(context)) { switch (flow f = _statement->execute(context); f.type()) { case flow_type::f_normal: case flow_type::f_continue: break; case flow_type::f_break: return f.consume_break(); case flow_type::f_return: return f; } } return flow::normal_flow(); } }; class for_statement: public for_statement_base { private: expression::ptr _expr1; public: for_statement( expression::ptr expr1, expression::ptr expr2, expression::ptr expr3, statement_ptr statement ): for_statement_base( std::move(expr2), std::move(expr3), std::move(statement) ), _expr1(std::move(expr1)) { } flow execute(runtime_context& context) override { _expr1->evaluate(context); return for_statement_base::execute(context); } }; class for_declare_statement: public for_statement_base { private: std::vector _decls; expression::ptr _expr2; expression::ptr _expr3; statement_ptr _statement; public: for_declare_statement( std::vector decls, expression::ptr expr2, expression::ptr expr3, statement_ptr statement ): for_statement_base( std::move(expr2), std::move(expr3), std::move(statement) ), _decls(std::move(decls)) { } flow execute(runtime_context& context) override { auto _ = context.enter_scope(); for (const expression::ptr& decl : _decls) { context.push(decl->evaluate(context)); } return for_statement_base::execute(context); } };

for_statement и for_statement_declare имплементирају се слично као while_statement и do_statement. Наслеђују се од for_statement_base класе, која чини већину логике. for_statement_declare се креира када је први део for -петље декларација променљиве.

Ц ++ Сторк: Имплементација изјава

Све су то класе изјава које имамо. Они су градивни елементи наших функција. Када runtime_context је створен, задржава те функције. Ако је функција декларисана кључном речи public, може се позвати именом.

То закључује основну функционалност Сторк-а. Све остало што ћу описати су накнадне мисли које сам додао да би наш језик био кориснији.

Туплес

Низови су хомогени контејнери, јер могу садржати само елементе једног типа. Ако желимо хетерогене контејнере, структуре нам одмах падну на памет.

Међутим, има више тривијалних хетерогених контејнера: тупле. Тупле могу задржати елементе различитих типова, али њихови типови морају бити познати током времена компајлирања. Ово је пример декларације корпице у Сторк-у:

[number, string] t = {22321, 'Siveric'};

Ово декларише пар number и string и иницијализује га.

Листе иницијализације могу се користити и за иницијализацију низова. Када се типови израза на иницијализацијској листи не подударају са типом променљиве, догодиће се грешка компајлера.

Пошто су низови имплементирани као контејнери variable_ptr, добили смо рунтиме имплементацију корпица бесплатно. Време је компајлирања када осигуравамо тачан тип садржаних променљивих.

Модули

Било би лепо сакрити детаље о примени од корисника Сторк-а и представити језик на једноставнији начин.

Ово је час који ће нам помоћи да то постигнемо. Представљам га без детаља о примени:

class module { ... public: template void add_external_function(const char* name, std::function f); template auto create_public_function_caller(std::string name); void load(const char* path); bool try_load(const char* path, std::ostream* err = nullptr) noexcept; void reset_globals(); ... };

Функције load и try_load учитаће и компајлира Сторк скрипту са дате путање. Прво, један од њих може да баци stork::error, али други ће га ухватити и одштампати на излазу, ако постоји.

Функција reset_globals поново ће покренути глобалне променљиве.

Функције add_external_functions и create_public_function_caller треба позвати пре састављања. Први додаје функцију Ц ++ која се може позвати из Сторк-а. Други креира објект који се може позвати и који се може користити за позивање функције Сторк из Ц ++. То ће изазвати грешку током компајлирања ако се тип јавне функције не подудара R(Args…) током компилације Сторк скрипте.

Додао сам неколико стандардних функција које се могу додати Сторк модулу.

void add_math_functions(module& m); void add_string_functions(module& m); void add_trace_functions(module& m); void add_standard_functions(module& m);

Пример

Ево примера Сторк скрипте:

function void swap(number& x, number& y) { number tmp = x; x = y; y = tmp; } function void quicksort( number[]& arr, number begin, number end, number(number, number) comp ) { if (end - begin <2) return; number pivot = arr[end-1]; number i = begin; for (number j = begin; j < end-1; ++j) if (comp(arr[j], pivot)) swap(&arr[i++], &arr[j]); swap (&arr[i], &arr[end-1]); quicksort(&arr, begin, i, comp); quicksort(&arr, i+1, end, comp); } function void sort(number[]& arr, number(number, number) comp) { quicksort(&arr, 0, sizeof(arr), comp); } function number less(number x, number y) { return x < y; } public function void main() { number[] arr; for (number i = 0; i < 100; ++i) { arr[sizeof(arr)] = rnd(100); } trace(tostring(arr)); sort(&arr, less); trace(tostring(arr)); sort(&arr, greater); trace(tostring(arr)); }

Ево дела Ц ++:

колико има сајтова за упознавање на мрежи
#include #include 'module.hpp' #include 'standard_functions.hpp' int main() { std::string path = __FILE__; path = path.substr(0, path.find_last_of('/\') + 1) + 'test.stk'; using namespace stork; module m; add_standard_functions(m); m.add_external_function( 'greater', std::function([](number x, number y){ return x > y; } )); auto s_main = m.create_public_function_caller('main'); if (m.try_load(path.c_str(), &std::cerr)) { s_main(); } return 0; }

Стандардне функције додају се модулу пре компајлирања, а функције trace и rnd користе се из Сторк скрипте. Функција greater је такође додат као излог.

Скрипта се учитава из датотеке „тест.стк“, која се налази у истој фасцикли као и „маин.цпп“ (употребом __FILE__ дефиниције претпроцесора), а затим функције main се зове.

У скрипти генеришемо насумични низ, сортирајући у растућем поређењу помоћу упоређивача less, а затим у опадајућем помоћу упоређивача greater, написаном на Ц ++.

Можете видети да је код савршено читљив за свакога ко течно говори Ц (или било који програмски језик изведен из Ц).

Шта даље?

Постоји много функција које бих желео да имплементирам у Сторк:

  • Структуре
  • Класе и наследство
  • Позиви између модула
  • Ламбда функције
  • Динамички откуцани објекти

Недостатак времена и простора један је од разлога зашто их већ нисмо применили. Покушаћу да ажурирам свој ГитХуб страница са новим верзијама док у слободно време имплементирам нове функције.

Окончање

Створили смо нови програмски језик!

То ми је одузело добар део слободног времена у протеклих шест недеља, али сада могу да напишем неке скрипте и видим да раде. То сам радио последњих дана, чешкајући се по ћелавој глави сваки пут кад би се неочекивано срушио. Понекад је то била мала грешка, а понекад гадна грешка. Међутим, понекад сам се осећао посрамљено јер се радило о лошој одлуци коју сам већ поделио са светом. Али сваки пут бих поправио и наставио да кодирам.

У процесу сам сазнао за if constexpr, који никада раније нисам користио. Такође сам се упознао са рвалуе-референцама и савршеним прослеђивањем, као и са другим мањим карактеристикама Ц ++ 17 са којима се не сусрећем свакодневно.

Код није савршен - никада не бих изнео такву тврдњу - али је довољно добар и углавном следи добру праксу програмирања. И што је најважније - успева.

Одлука да нови језик развијете од нуле може звучати лудо за просечну особу, па чак и за просечног програмера, али утолико је већи разлог што то радите и доказујете себи да то можете. Баш као што је решавање тешке загонетке добра вежба за мозак како бисте остали ментално способни.

Тупи изазови су чести у нашем свакодневном програмирању, јер не можемо да одаберемо само његове занимљиве аспекте и морамо озбиљно да се бавимо чак и ако је понекад досадно. Ако сте професионални програмер, ваш први приоритет је испорука висококвалитетног кода вашем послодавцу и стављање хране на сто. То вас понекад може навести да избегавате програмирање у слободно време и може пригушити ентузијазам ваших раних школских дана програмирања.

Ако не морате, не губите тај ентузијазам. Порадите на нечему ако вам се чини занимљивим, чак и ако је то већ учињено. Не морате да оправдавате разлог да бисте се забавили.

А ако то можете - чак делимично - да укључите у свој професионални рад, добро за вас! Нема много људи ту прилику.

Код за овај део биће замрзнут са посебном граном на мом ГитХуб страница .

Разумевање основа

Шта је изјава у рачунарском програмирању?

Изјава је најмања јединица рачунарског програма која се може извршити.

Која је главна разлика између низова и корпица?

Низови садрже елементе истог типа, док тупле могу садржати елементе различитих типова.

Шта је бајт код?

У неким програмским језицима, бајт код је резултат компилације, која се састоји од инструкција на нижем нивоу које тумач може да изврши.

Мој преглед ЦакеПХП 3 - још увек свеж, још увек врућ

Технологија

Мој преглед ЦакеПХП 3 - још увек свеж, још увек врућ
Уговори Етхереум Орацле: Својства кода солидности

Уговори Етхереум Орацле: Својства кода солидности

Бацк-Енд

Популар Постс
Позив на акцију: пословни модел на захтев
Позив на акцију: пословни модел на захтев
Реактивност на захтев у Вуе 3
Реактивност на захтев у Вуе 3
10 најчешћих грешака у покретању покрета које програмери праве
10 најчешћих грешака у покретању покрета које програмери праве
Америчка програмерка Рацхелл Цалхоун освојила је пету АпееСцапе стипендију
Америчка програмерка Рацхелл Цалхоун освојила је пету АпееСцапе стипендију
Водич за вишепроцесорске моделе мрежних сервера
Водич за вишепроцесорске моделе мрежних сервера
 
3Д графика: Водич за ВебГЛ
3Д графика: Водич за ВебГЛ
Почевши од криптосистема СРВБ
Почевши од криптосистема СРВБ
Орацле за СКЛ Сервер и СКЛ Сервер за Орацле Мигратион Гуиде - Пт. 3
Орацле за СКЛ Сервер и СКЛ Сервер за Орацле Мигратион Гуиде - Пт. 3
Дос и ане стратегије поновног брендирања
Дос и ане стратегије поновног брендирања
Како са Флекбок-ом направити паметне распореде само за ЦСС
Како са Флекбок-ом направити паметне распореде само за ЦСС
Популар Постс
  • питхон шта је атрибут
  • с цорп ц цорп ллц
  • процуреле кредитне картице са новцем
  • гешталт принцип близине односи се на начин на који ми
  • бесплатни хаковани бројеви кредитних картица са цвв-ом
  • модули онемогућавају тимски рад програмера.
Категорије
  • Процес И Алати
  • Рисе Оф Ремоте
  • Профитабилност И Ефикасност
  • Инжењерски Менаџмент
  • © 2022 | Сва Права Задржана

    portaldacalheta.pt