Стажировка в JetBrains и как мне почти удалось попасть на неё

  • Tutorial
image

Как и многие молодые разработчики, когда появляется желание найти работу/стажировку — я смотрю в сторону крутых IT компаний.

Недавно я попробовал попасть в ряды JetBrains и под катом готов поделиться полученным опытом.

Почему «почти» удалось?


Наверняка сразу у вас встает такой вопрос.

На мой взгляд у меня имеется неплохое резюме с кучей ачивок и хороший скилл, который я день за днем совершенствую последние 8-9 лет.

Я выполнил тестовое задание (и как мне кажется хорошо), ранее посещал офис JB, который благо находится в моем городе, общался с HH и некоторыми разработчиками компании и в итоге получил отказ на стажировку без каких-либо комментариев.

Скорее всего причина таится в том, что на стажировку JetBrains отбирает исключительно студентов, а я на данный момент только выпустился из 11-го и сдаю один за другим экзамены.

Что ж, это повод в течении ещё целого года поднатаскать себя и подать заявку на следующий год.

Разбор тестового задания


Сроки подачи заявок на стажировку и проверки тестовых заданий закончились, а это значит, что все, кто их решил, включая меня — могут выложить разбор этих заданий, чтобы в следующем году любой желающий студент мог перед началом стажировок JB ознакомиться с примерным уровнем заданий, с которыми ему придется столкнуться и в случае чего подтянуть свои знания.

Я подавал заявку на стажировку в команду разработки отладчика корутин для Kotlin.

Задачей этой команды на стажировке у тех, кто на неё попал в этом году будет доработка этой части отладчика и её интеграция с IDE.

Задание было немного ожидаемым — написать отладчик для небольшого ЯП.

Я бы не сказал, что оно сложное, скорее наоборот. Оно не требует каких-либо глубоких знаний теории построения трансляторов и крутого скилла. Но тем не менее, те, кто подают заявку на стажировку по этому направлению, как минимум должны владеть этими основами и без проблем справиться с этим заданием. Я был удивлен, когда решил поискать на github'е по ключевым словам решения моих «конкурентов» и нашел 1-2 более-менее на вид рабочих решения против около 6-7 пустых репозиториев либо с парой кусков кода, после которых люди опустили руки. Возможно я плохо искал, но тем не менее результаты меня не порадовали. Если этот пост будут читать люди, которые забросили это задание — не надо в будущем так делать. В крайнем случае достаточно было посидеть над заданием пару дней и я уверен — вы бы с ним справились.

Сам текст задания
Задача: реализовать пошаговое исполнение кода для тривиального языка программирования Guu.

Внимание: в описании ниже заведомо опущены некоторые существенные моменты. Как правило, они остаются на ваше усмотрение. Если будет совсем непонятно, пишите на (тут почта, которую я решил убрать).

Программа на Guu состоит из набора процедур. Каждая процедура начинается со строки sub (subname) и завершается объявлением другой процедуры (или концом файла, если процедура в файле последняя). Исполнение начинается с sub main.

Тело процедуры – набор инструкций, каждая из которых находится на отдельной строке. В начале строки могут встречаться незначимые символы табуляции или пробелы. Пустые строки игнорируются. Комментариев в Guu нет.

В Guu есть лишь три оператора: — set (varname) (new value) – задание нового целочисленного значения переменной. — call (subname) – вызов процедуры. Вызовы могут быть рекурсивными. — print (varname) – печать значения переменной на экран.

Переменные в Guu имеют глобальную область видимости. Программа ниже выведет на экран строку a = 2.

sub main
set a 1
call foo
print a

sub foo
set a 2

А вот простейшая программа с бесконечной рекурсией:

sub main
call main

Необходимо написать пошаговый интерпретатор для Guu. При его запуске отладчик должен останавливаться на строчке с первой инструкцией в sub main и ждать команд от пользователя. Минимально необходимый набор команд отладчика:

i – step into, отладчик заходит внутрь call (subname).
o – step over, отладчик не заходит внутрь call.
trace – печать stack trace исполнения с номерами строк, начиная с main…
var – печать значений всех объявлённых переменных.

Формат общения пользователя с отладчиком остаётся на выше усмотрение. Вы можете выбрать как минималистичный GDB-like интерфейс, так и консольный или графический UI. Названия команд отладчика можно при желании изменить.

Для решения этой задачи вы можете использовать любой язык программирования из TIOBE TOP 50 и open-source компилятором/интерпретатором.

При оценке работы будет оцениваться:

Общая работоспособность программы;
Качество исходного кода и наличие тестов;
Простота расширения функциональности (например, поддержка новых операторов языка или инструкций отладчика).
Решение с инструкцией по его сборке нужно опубликовать в Git-репозиторий (например, на GitHub или BitBucket). В ответе нужно указать ссылку на репозиторий. Подойдёт и ссылка на приватный GitHub-репозиторий, только в него потребуется добавить меня.

Я пишу на C++, Java и Object Pascal.

Сначала были мысли написать все на моем же ЯП (Mash), но я подумал, что это будет не очень удобно проверять сотруднику JB, да и заявку я подал за 2 дня до закрытия подачи (экзамены все-же...), да и за окном уже был вечер — решил я все быстренько написать на более известных языках.

Для решения задачи Pascal на мой взгляд подходит больше всего, как минимум из-за наиболее удобной реализации строк…

По крайней мере для меня. К тому же он находится в TIOBE TOP 50, так что я смело запустил IDE, а именно — Lazarus, т.к. он не коммерческий :) и приступил к решению задачи.

Несмотря на то, что JB дают аж целых 7 дней, по времени у меня в сумме ушло около часа, а проект получился примерно в 500 строк кода.

С чего начать?


Прежде всего нужно представить, как будет в итоге работать отладка кода.

Нам нужно реализовать пошаговое выполнение кода — значит каждая инструкция должна быть представлена в виде структуры/класса и в общем инструкции должны выглядеть как список этих классов или, как в моей реализации — ссылаться друг на друга образуя последовательность (позже распишу почему я так сделал).

Чтобы получить эту последовательность, нашему отладчику нужно обработать код на предложенном языке, значит нам также нужно реализовать небольшой парсер, а также синтаксический и семантический анализ кода.

Начнем с реализации парсера. Т.к. язык Guu состоит из набора токенов, разделяемых пробелом, то логично первым делом написать небольшой и простой токенайзер:

function GetToken(s: string; tokenNum: word): string;
var
  p: word;
begin
  s := Trim(s);
  s := StringReplace(s, '  ', ' ', [rfReplaceAll]);

  while tokenNum > 1 do
   begin
     p := Pos(' ', s);
     if p > 0 then
       Delete(s, 1, p)
     else
       begin
         s := '';
         break;
       end;
     dec(tokenNum);
   end;

  p := Pos(' ', s);
  if p > 0 then
    Delete(s, p, Length(s));

  Result := s;
end;

Далее объявляем enum из токенов:

type
  TGuuToken = (opSub, opSet, opCall, opPrint, opUnknown);

const
  GuuToken: array[opSub..opPrint] of string = (
    'sub', 'set', 'call', 'print'
  );

И сам класс инструкции, в которую будем разбирать строки кода:

type
  TGuuOp = class
    public
      OpType         : TGuuToken;
      OpArgs         : TStringList;
      OpLine         : Cardinal;
      OpUnChangedLine: string;
      NextOp         : TGuuOp;
      OpReg          : Pointer;
      function    Step(StepInto: boolean; CallBacks: TList; Trace: TStringList): TGuuOp;
      constructor Create(LineNum: Cardinal; Line:string);
      destructor  Destroy; override;
  end;

В OpType будет храниться инструкция, в OpArgs — остальные части конструкции.
OpLine, OpUnChangedLine — информация для отладчика.

NextOp — указатель на следующую инструкцию. Если он равен nil (null в Pascal), то далее нет инструкций и нужно завершить выполнение кода, либо вернуться по callback стеку.

OpReg — небольшой указатель-регистр, который будет использоваться далее для небольшой оптимизации выполнения кода.

После того, как было написано объявление класса — я решил, что наиболее компактным и красивым решением было бы дописать парсер и небольшой синтаксический анализ в его конструкторе, что я дальше и сделал:

constructor TGuuOp.Create(LineNum: Cardinal; Line:string);
(*
 * That method parse code line.
 *)
var
  s: string;
  w: word;
begin
  inherited Create;
  OpArgs := TStringList.Create;
  OpLine := LineNum;
  OpUnChangedLine := Line;

  NextOp    := nil;
  OpReg     := nil;

  s := GetToken(Line, 1);
  OpType := TGuuToken(AnsiIndexStr(s, GuuToken));
  case OpType of
    opSub  : begin // sub <name>
               s := GetToken(Line, 2);
               if Length(s) > 0 then
                OpArgs.Add(s)
               else
                begin
                  writeln('[Syntax error]: Invalid construction "sub" at line ', OpLine, '.');
                  halt;
                end;

               if Length(GetToken(Line, 3)) > 0 then
                begin
                  writeln('[Syntax error]: Invalid construction "', Line, '" at line ', OpLine, '.');
                  halt;
                end;
             end;

    opSet  : begin // set <var> <value>
               OpArgs.Add(GetToken(Line, 2));
               OpArgs.Add(GetToken(Line, 3));
               w := 1;
               while w < Length(OpArgs[1]) + 1 do
                begin
                  if not (OpArgs[1][w] in ['0'..'9']) then
                   begin
                     writeln('[Syntax error]: Invalid variable assigment "', Line, '" at line ', OpLine, '.');
                     halt;
                   end;
                  inc(w);
                end;

               if (Length(OpArgs[0]) = 0) or (Length(OpArgs[1]) = 0) or
                  (Length(GetToken(Line, 4)) > 0) then
                begin
                  writeln('[Syntax error]: Invalid construction "', Line, '" at line ', OpLine, '.');
                  halt;
                end
             end;

    opCall : begin // call <name>
               s := GetToken(Line, 2);
               if Length(s) > 0 then
                OpArgs.Add(s)
               else
                begin
                  writeln('[Syntax error]: Invalid construction "call" at line ', OpLine, '.');
                  halt;
                end;

               if Length(GetToken(Line, 3)) > 0 then
                begin
                  writeln('[Syntax error]: Invalid construction "', Line, '" at line ', OpLine, '.');
                  halt;
                end;
             end;

    opPrint: begin // print <var>
               s := GetToken(Line, 2);
               if Length(s) > 0 then
                OpArgs.Add(s)
               else
                begin
                  writeln('[Syntax error]: Invalid construction "print" at line ', OpLine, '.');
                  halt;
                end;

               if Length(GetToken(Line, 3)) > 0 then
                begin
                  writeln('[Syntax error]: Invalid construction "', Line, '" at line ', OpLine, '.');
                  halt;
                end;
             end;
    else
      begin
        writeln('[Syntax error]: Invalid token "', s, '" at line ', OpLine, '.');
        halt;
      end;
  end;
end;

destructor  TGuuOp.Destroy;
begin
  FreeAndNil(OpArgs);
  inherited;
end;

Тут мы по сути проверяем начало конструкции (т.е. первое слово), а затем смотрим на остальные токены и их количество. Если с кодом что-то явно не правильно — выводим ошибку.

В главном куске кода мы просто читаем из файла код в TStringList, построчно вызываем конструкторы TGuuOp и сохраняем указатели на экземпляры классов в GuuOps: TList.

Объявления:

var
  LabelNames: TStringList;
  GuuOps, GuuVars: TList;

  SubMain: TGuuOp = nil;

Совместно с парсингом кода хорошо бы выполнить ещё пару действий:

procedure ParseNext(LineNum: Cardinal; Line: string);
(*
 * Parsing code lines and define variables and labels.
 *)
var
  Op: TGuuOp;
  GV: TGuuVar;
  c: cardinal;
begin
  if Trim(Line) <> '' then
   begin
     Op := TGuuOp.Create(LineNum, Line);
     GuuOps.Add(Op);

     case Op.OpType of
      opSet: begin // define variable and/or optimisation var calling
               GV := nil;
               c := 0;
               while c < GuuVars.Count do
                begin
                  if TGuuVar(GuuVars[c]).gvName = Op.OpArgs[0] then
                   begin
                     GV := TGuuVar(GuuVars[c]);
                     break;
                   end;
                  inc(c);
                end;

               if GV = nil then
                begin
                  GV := TGuuVar.Create(Op.OpArgs[0]);
                  GuuVars.Add(GV);
                end;

               Op.OpReg := GV;
             end;

      opSub: begin // Check for label dublicade declaration
               if Op.OpArgs[0] = 'main' then
                SubMain := Op;

               if LabelNames.IndexOf(Op.OpArgs[0]) <> -1 then
                begin
                  writeln('[Error]: Dublicate sub "', Op.OpArgs[0], '" declaration at line ', Op.OpLine, '.');
                  halt;
                end
               else
                LabelNames.Add(Op.OpArgs[0]);
             end;
     end;
   end;
end;

На данном этапе можно проверить точки входа на момент переопределения и вспомнить про OpReg — его я использовал для хранения указателя на Guu переменную.

К слову о переменных — вынес этот небольшой кусок кода в отдельный юнит:

unit uVars;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils;

type
  TGuuVar = class
    public
      gvName: string;
      gvVal: variant;
      constructor Create(VarName: string);
  end;

implementation

constructor TGuuVar.Create(VarName: string);
begin
  inherited Create;
  gvName := VarName;
  gvVal := 0;
end;

end.

Теперь у нас есть распарсенный код, который по синтаксису вроде бы правильный. Осталось его проанализировать и можно приступать к выполнению и самому главному — отладке.

Далее следует реализовать небольшой семантический анализ и попутно подготовить все к выполнению и отладке кода:

procedure CheckSemantic;
(*
 * Semantic analyse and calls optimisation.
 *)
var
  c, x: cardinal;
  op: TGuuOp;
begin
  if GuuOps.Count > 0 then
   begin
     if TGuuOp(GuuOps[0]).OpType <> opSub then
      begin
        writeln('[Error]: Operation outside sub at line ', TGuuOp(GuuOps[0]).OpLine, '.');
        halt;
      end;

     c := 0;
     while c < GuuOps.Count do
      begin
        case TGuuOp(GuuOps[c]).OpType of
          opSub:;

          opCall: begin
                    TGuuOp(GuuOps[c - 1]).NextOp := TGuuOp(GuuOps[c]);
                    x := 0;
                    op := nil;
                    while x < GuuOps.Count do
                     begin
                       if TGuuOp(GuuOps[x]).OpType = opSub then
                       if TGuuOp(GuuOps[x]).OpArgs[0] = TGuuOp(GuuOps[c]).OpArgs[0] then
                        begin
                          op := TGuuOp(GuuOps[x]);
                          break;
                        end;
                      inc(x);
                    end;

                   if op <> nil then
                    TGuuOp(GuuOps[c]).OpReg := op
                   else
                    begin
                      writeln('[Error]: Calling to not exist sub "', TGuuOp(GuuOps[c]).OpArgs[0],
                              '" at line ', TGuuOp(GuuOps[c]).OpLine, '.');
                      halt;
                    end;
                 end;

          opPrint: begin
                     TGuuOp(GuuOps[c - 1]).NextOp := TGuuOp(GuuOps[c]);
                     x := 0;
                     while x < GuuVars.Count do
                      begin
                        if TGuuVar(GuuVars[x]).gvName = TGuuOp(GuuOps[c]).OpArgs[0] then
                         begin
                           TGuuOp(GuuOps[c]).OpReg := TGuuVar(GuuVars[x]);
                           break;
                         end;
                        inc(x);
                      end;

                     if TGuuOp(GuuOps[c]).OpReg = nil then
                      begin
                        writeln('[Error]: Variable "', TGuuOp(GuuOps[c]).OpArgs[0],
                                '" for print doesn''t exist at line ', TGuuOp(GuuOps[c]).OpLine, '.');
                      end;
                   end;
          else
            TGuuOp(GuuOps[c - 1]).NextOp := TGuuOp(GuuOps[c]);
        end;
        inc(c);
      end;
   end;
end;

В TGuuOp.NextOp каждого токена записываем указатель на следующий за ним токен.
Для опкода call делаем все хитро и просто — в NextOp записываем указатель на вызываемую точку входа.

Также проверяем выводимые переменные через инструкцию print…

Может быть их не объявили перед выводом?

Теперь нужно реализовать выполнение кода. Возвращаемся к классу TGuuOp и реализуем метод Step:

function TGuuOp.Step(StepInto: boolean; CallBacks: TList; Trace: TStringList): TGuuOp;
(*
 * That method execute instruction.
 *)
var
  Op: TGuuOp;
  CBSize: Cardinal;
begin
  case OpType of
    opSub: begin
             Trace.Add('-> Sub "' + OpArgs[0] + '"');
             Result := NextOp;
           end;

    opCall: begin
              if StepInto then
               begin
                 if NextOp <> nil then
                  CallBacks.Add(NextOp);
                 Result := TGuuOp(OpReg);
               end
              else
               begin
                 Op := TGuuOp(OpReg);
                 CBSize := CallBacks.Count;

                 while ((Op <> nil) or (CallBacks.Count > CBSize)) and (Trace.Count < STACK_SIZE) do
                  begin
                    if Op = nil then
                     begin
                       Op := TGuuOp(CallBacks[CallBacks.Count - 1]);
                       CallBacks.Delete(CallBacks.Count - 1);
                       Trace.Delete(Trace.Count - 1);
                     end;

                    Op := Op.Step(StepInto, CallBacks, Trace);
                  end;

                 Result := NextOp;
               end;
            end;

    opPrint: begin
               writeln(TGuuVar(OpReg).gvName, ' = ', TGuuVar(OpReg).gvVal);
               Result := NextOp;
             end;

    opSet: begin
             TGuuVar(OpReg).gvVal := OpArgs[1];
             Result := NextOp;
           end;
  end;
end;

Чтобы избежать access violation в случае зацикливания — лучше ограничить стек, что я и сделал.
Константа STACK_SIZE = 2048, объявленная выше как раз отвечает за это.

Теперь наконец настало время написать основной код нашего отладчика:

var
  code: TStringList;
  c: Cardinal;
  cmd: string;
  CallBacks: TList;
  Trace: TStringList;
  DebugMode: boolean = true;
begin
  if ParamCount > 0 then
    begin
      // Initialisation

      if not FileExists(ParamStr(1)) then
       begin
         writeln('[Error]: Can''t open file "', ParamStr(1), '".');
         halt;
       end;

      if ParamCount > 1 then
       if LowerCase(ParamStr(2)) = '/run' then
        DebugMode := false;

      code := TStringList.Create;
      code.LoadFromFile(ParamStr(1));

      GuuOps  := TList.Create;
      GuuVars := TList.Create;

      // Parsing and preparing

      LabelNames := TStringList.Create;

      c := 0;
      while c < code.Count do
       begin
         ParseNext(c + 1, Trim(code[c]));
         inc(c);
       end;

      FreeAndNil(LabelNames);

      CheckSemantic;

      if SubMain = nil then
       begin
         writeln('[Error]: Sub "main" doesn''t exist!');
         halt;
       end;


      // Start code execution

      CurrentOp := SubMain;

      CallBacks := TList.Create;
      Trace := TStringList.Create;

      if DebugMode then
       begin
         //Out code and features

         ClrScr;
         writeln('Code for debugging:');
         writeln('.....');
         c := 0;
         while c < code.Count do
          begin
            writeln(FillSpaces(IntToStr(c + 1), 4), '| ', code[c]);
            inc(c);
          end;
         writeln('"""""');

         FreeAndNil(code);

         writeln(sLineBreak,
                 'Features:', sLineBreak,
                 '* i     - step into.', sLineBreak,
                 '* o     - step over.', sLineBreak,
                 '* trace - print stack trace.', sLineBreak,
                 '* var   - print variables list.', sLineBreak,
                 '* x     - exit.', sLineBreak);

         // Execution loop
         while ((CurrentOp <> nil) or (CallBacks.Count > 0)) and (Trace.Count < STACK_SIZE) do
          begin
            write('Line ', CurrentOp.OpLine, ' ~> ');
            readln(cmd);

            // Execute commands
            if cmd = 'i' then
             CurrentOp := CurrentOp.Step(true, CallBacks, Trace)
            else
            if cmd = 'o' then
             CurrentOp := CurrentOp.Step(false, CallBacks, Trace)
            else
            if cmd = 'trace' then
             begin
               writeln('| Trace:');
               c := 0;
               while c < Trace.Count do
                begin
                  writeln('| ', Trace[c]);
                  inc(c);
                end;
               writeln('| -> Line ', CurrentOp.OpLine, ': "', CurrentOp.OpUnChangedLine, '".')
             end
            else
            if cmd = 'var' then
             begin
               writeln('| Variables list:');
               c := 0;
               while c < GuuVars.Count do
                begin
                  writeln('| ', TGuuVar(GuuVars[c]).gvName, ' = ', TGuuVar(GuuVars[c]).gvVal);
                  inc(c);
                end;
             end
            else
            if cmd = 'x' then
             halt;

            // Check for method end & make callback
            if (CurrentOp = nil) and (CallBacks.Count > 0) then
             begin
               CurrentOp := TGuuOp(CallBacks[CallBacks.Count - 1]);
               CallBacks.Delete(CallBacks.Count - 1);
               Trace.Delete(Trace.Count - 1);
             end;
          end;
       end
      else
       begin
         // Only run mode (/run)
         FreeAndNil(code);

         while ((CurrentOp <> nil) or (CallBacks.Count > 0)) and (Trace.Count < STACK_SIZE) do
          begin
            CurrentOp := CurrentOp.Step(false, CallBacks, Trace);
            if (CurrentOp = nil) and (CallBacks.Count > 0) then
             begin
               CurrentOp := TGuuOp(CallBacks[CallBacks.Count - 1]);
               CallBacks.Delete(CallBacks.Count - 1);
               Trace.Delete(Trace.Count - 1);
             end;
          end;
       end;

      if Trace.Count >= STACK_SIZE then
       writeln('[Runtime error]: Stack overflow!');

      FreeAndNil(CallBacks);
      FreeAndNil(Trace);
    end
  else
    writeln(
      'Guu debugger v1.0.', sLineBreak,
      'Author: Pavel Shiryaev (@RoPi0n).', sLineBreak,
      'Run: svmc guu_debugger.vmc <guu source file> [arg]', sLineBreak,
      'Args:', sLineBreak,
      ' /run - Run Guu code.'
    );
end.

По условию задания интерфейс можно реализовать как угодно.

Можно было бы реализовать полноценный UI, прикрутить SynEdit к проекту, но на мой взгляд — это пустой труд, который не отразит скилл, да и к тому же, за который не заплатят :)

Так что я ограничился небольшим консольным UI.

Код выше не является чем-то сложным, так что его можно оставить без комментариев. В нем мы берем готовые TGuuOp'сы и вызываем их Step.

Скрины решенной задачки:

image

image

Вывод информации об ошибках:

image

image

Ссылка на репозиторий моего решения: клац

Итоги


Итогов особо нет. Придется мне посвятить большую часть лета насыщенному отдыху и поиску вуза (ну, на случай если ЕГЭ я сдам хорошо, конечно), вместо двух месяцев работы и обучения в команде JetBrains.

Возможно в следующем году на Хабре появится новый пост, уже описывающий процесс стажировки в JB либо в другой интересной мне компании :)

Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 78

    +12
    Попытка достойна одобрения, но
    и как мне кажется хорошо
    Не обижайтесь, но Вам неправильно кажется. Даже будучи рабочим, код не очень хорошо структурирован и не отвечает требованиям «качественный» и «легко расширяемый».
    1) Лучше вместо класса операции создать классы для каждой отдельной операции и отнаследовать от базового. Тогда огромные switch'и сразу превращаются в хорошо локализированые реализации каждого из типов инструкции.
    2) Код главного цикл приложения слишком длинный, ориентироваться сложно — нужно повыносить в процедуры/функции.
    3) GetToken ужасно неэффективный из-за постоянной модификации входной строки.
    4) Ну и если уж это ТЗ в JetBrains, то стоило писать на Java :)
      +5
      У меня есть странное ощущение, что 4-й пункт сыграл очень большую роль. HR-а мог легко отпугнуть код на Паскале, когда вся разработка – на Java.

      Ну и немного моего личного брюзжания (потому что мне в некоторой степени это всё знакомо по личному опыту):
      После фразы:
      который я день за днем совершенствую последние 8-9 лет.

      не ожидаешь увидеть такое:
      а я на данный момент только выпустился из 11-го


      Даже если вы и начали заниматься программированием в 10 лет, как правило, этот опыт нерелевантен для HR/резюме. Лучше уменьшите его до 2-3, и станет лучше. До 16-17 очень сложно набрать по-настоящему профессиональные навыки, а не просто изучить язык и написать несколько программ.

      Это сугубо ИМХО, я могу ошибаться и надеюсь меня поправят в случае чего :)

      P.S. Код лучше засунуть под спойлеры.
        +2
        Ну как сказать, у меня опыт с 5 класса на С++/Pascal. И когда на собесе в одной компании, которую все* знают, гоняли по алгоритмам, все же не пустой опыт. Ах, да, когда это все было я был несовершеннолетним с 2 годами коммерческого опыта. Поэтому не стоит скрывать реальное рвение к знаниям и целеустремленность, если человек много лет этим занимается и ему это в кайф, чего тут стесняться?

        *все=многие
          0
          если человек много лет этим занимается и ему это в кайф, чего тут стесняться?

          Я не говорил про «стесняться». Я говорил про целесообразность добавления таких формулировок в такую весьма формальную вещь как резюме. Рвение к знаниям и целеустремленность – это хорошо, но совершенно никак не выделяет вас из толпы других кандидатов. В конце концов, это само собой разумеющееся требование к работнику в IT. Кому нужен сотрудник без рвения к знаниям и нецелеустремленный?
          Странно будет, если я напишу, что у меня опыт в IT около 15 лет, ведь рваться к знаниям я начал в 7 :-)

          В живом диалоге с HR'ом, когда есть возможность поговорить о себе, об этом стоит упомянуть, я согласен, но к реальному опыту, интересующему фирму, это не имеет особо отношения.

          Вот коммерческий опыт – это хороший козырь, да. Это показатель того, что вас особо не придется учить азам поведения в команде, работе с тикетами и всему такому прочему.

          И когда на собесе в одной компании, которую все* знают, гоняли по алгоритмам, все же не пустой опыт.

          А что за алгоритмы там были и про что спрашивали? Меня терзают сомнения, что вы благодаря школьному опыту отлично знали всякие поисковые деревья, их балансировку и всё такое. Мне правда интересно, без попытки выставить вас лжецом.
            0
            Сортировка особенных данных, нечёткий поиск данных в словаре и прочие, которые должен(кому?) знать каждый программист.

            P.s. В школе не дали ни грамма знаний в области IT.
              0
              После вашего P.S. я не понимаю, к чему тогда была вот эта фраза:
              Ну как сказать, у меня опыт с 5 класса на С++/Pascal. И когда на собесе в одной компании, которую все* знают, гоняли по алгоритмам, все же не пустой опыт.

              Либо вы не так выразились, либо я неправильно воспринял.
                0
                5 класс = 10 лет, стоило так сразу и написать. Просто ваш комментарий попал прямо в точку, я начал заниматься именно в 10 лет. Основная мысль: любой опыт ценен и согласен важно правильно сделать акценты.
          +1
          У меня есть странное ощущение, что 4-й пункт сыграл очень большую роль. HR-а мог легко отпугнуть код на Паскале, когда вся разработка – на Java.

          Справедливости ради, в задании сказано, что любой язык из TIOBE топ 50 подойдет (а Паскаль там на 12-м месте, внезапно).
            +8
            В жизни никогда не стоит забывать про то, насколько сильно влияет человеческий фактор на принятие решений.

            Наверно, к стыду своему я скажу, что скорее бы рассмотрел резюме человека, решившего задачу на C#/C++, чем на Паскале. Просто потому, что решение на Паскале может значить, что человек не знает текущих «актуальных» энтерпрайз-языков, а времени обучать его может у компании и не быть.

            Но да, вы правы, формально Паскаль тоже допускается.
              0
              Во-первых, это роль стажера, куда, по-видимому, берут школьников, которые вполне себе могут кодить на Паскале. Во-вторых, если знание какого-то конкретного языка важно, это надо учесть при составлении задания. Короче, я не вижу причин пытаться быть телепатом и угадать, к чему там предвзяты интервьюеры. Если они не могут составить простейшие требования, то в работе все будет еще хуже.
                +1
                Летняя стажировка в JetBrains — это 2 месяца оплачиваемой работы и обучения под руководством опытных разработчиков различных продуктов компании. Студенты получают уникальную возможность стать полноценными членами продуктовых команд, работая над решением интересных проблем с высокой степенью самостоятельности и ответственности.

                Цитата с их сайта. Предположу, что студентов они хотят видеть 2-3-4 курса (как правило, по опыту с другими фирмами), насчет школьников ничего не сказано, поэтому чистой воды телепатия.

                Но я бы на их месте указал язык, который они хотят видеть при решении задачи. Возможно, я действительно неправ и Паскаль был последним, что им не понравилось :)
            +2
            HR-а мог легко отпугнуть код на Паскале, когда вся разработка – на Java.

            Для справки: тестовые задания проверяют непосредственно будущие менторы. Ну и я сильно сомневаюсь, что HR-ов в JB может что-то напугать ;)
            +5
            1) Очень плохой тон со стороны JB, не указав причины отказа, компанию это не красит. Сколько об этом уже было сказано, а воз и ныне там…
            2)
            Ну и если уж это ТЗ в JetBrains, то стоило писать на Java
            может это надо было явно указать, а не давать на откуп исполнителя, вам так не кажется?
              0
              Разумеется, язык в такой ситуации должен быть явно указан, ну или хотя бы примерный перечень. А претенденту в данной ситуации нужно подумать — чего от него ждут.
              На счет отсутствия обратной связи — автор ведь сам сказал, что JetBrains — «крутая» компания. Вот и с претендентами обращаются соответствующим образом :)
              • UFO just landed and posted this here
                  +1
                  Так надо призвать их в комменты.
                  yegnau
                  advertka
                  moscas
                    +2
                    Я лично про стажировки прокомментировать не смогу, но спрошу у коллег.
                    +1

                    JB отличная компания. Но судя по всему в приложении internship.jetbrains.com пока не предусмотрены фидбеки (на то оно и в бете пока). Или по другим причинам я их не получил.

                    +5
                    чего от него ждут
                    ну давайте еще на кофейной гуще гадать…
                    JetBrains — «крутая» компания. Вот и с претендентами обращаются соответствующим образом
                    давать фидбек — это признак хорошего тона. А отсутствие фидбека только портит репутацию и свою адекватность. Если человек адекватен, то он понимает свою ценность и ценность той, компании, где он хочет приносить пользу, а не смотрит на всякие клише, типа крутости компании или сеньерности своей должности. Коллеги, уважайте себя, не ведитесь на «крутость», будьте адекватнее, жизнь на этом не заканчивается)
                      +1
                      давать фидбек — это признак хорошего тона
                      Фидбек — это ещё и показатель того, как компания относится к тем, кто ей не нужен. Условно, можно сколько угодно облизывать разработчика, которого хотят нанять, кормить бонусами тех, кто ещё нужен на работе. Но если компания не готова даже дать фидбек, имхо, это сигнал о том, что она приложит все усилия, чтобы выгнать ставшего ненужным сотрудника максимально быстро и с минимальными затратами.
                    +5
                    Давайте поясню, как человек изнутри.

                    Во-первых, стажёров на летнюю практику набирает не JetBrains, а JetBrains Research. Это отдельная организация (хоть и финансируемая JetBrains) со своей структурой. HR-отдел JetBrains совершенно не занимается стажировками. Во-вторых, стажёров набирают конкретные менторы. То есть любой штатный сотрудник JetBrains имеет право объявить задачу на стажировку и объявить свой собственный способ отбора студента или студентов на эту задачу. Там есть некоторые ограничения (например, сперва идёт тестовое задание, а потом собеседование), но в целом этот один человек всё решает: и как формулировать задание, и по каким критериям его проверять, и кого звать на собеседование, и кого в итоге пригласить на стажировку. Студент, желающий проходить стажировку, подаётся именно на конкретный проект. Может податься на 2-3 проекта, но на каждом будет своё тестовое задание и свой ментор. Важно то что в итоге на каждом проекте место обычно одно, больше — редко.

                    К примеру, в прошлом году я брал стажёра. Я дал одну задачу, указав, что решение обязательно должно быть на Java. Мне прислали 16 решений, шестерых я пригласил на собеседование и одного в итоге взял. Я не уверен, высылал ли я каждому из десяти не прошедших на собеседование мотивированный отказ, хотя у меня есть заметки по каждому решению. Но если бы меня спросили, я бы, конечно, объяснил недостатки решения. Пятерым не прошедшим собеседование я тоже никак не мотивировал отказ. В принципе как тут мотивируешь? Они все молодцы, неплохо справились с тестовым заданием, показали довольно приличный для студента уровень знаний, но нашёлся среди них человек, который показал более высокий уровень знаний и опыт. Это же не экзамен, а соревнование, позиция всего одна.

                    Я бы поостерёгся разрешать писать на любом языке из top-50. Когда присылают 16 решений, их будет очень сложно сравнить. Если язык мне совсем незнаком, мне придётся разбираться, как это скомпилировать и запустить. Если я не смогу в итоге запустить, это я корявый, не ту версию компилятора поставил, или это решение корявое? Могу ли я оценить качество кода на незнакомом мне языке? Если в итоге мой проект на Java, а человек демонстрирует уверенное знание PHP, как я пойму, насколько он хорошо будет писать на Java? Мне кажется, что ограничить язык (а лучше и версию) разумно. Но тут каждому ментору виднее, конечно.
                      +7
                      Благодарю за пояснение, но хотел бы прокомментировать некоторые ваши высказывания.
                      не JetBrains, а JetBrains Research. Это отдельная организация
                      вы серьезно считаете, что в это будет кто-то вникать, особенно школьники? Ну что вы, в самом то деле… Название компании как бренд упоминается, этого достаточно
                      Но если бы меня спросили, я бы, конечно, объяснил недостатки решения
                      а вы не пытались при этом вспомнить себя? когда вы делали тестовые задания и вам ничего не отвечали, от слова совсем, вы что, всем писали, чтобы вам прокомментировали отказ? Уверен, что нет, что никому не писали, но уверен, что очень хотели бы, чтобы вам написали подробности, что и как не так вы сделали. Так почему же вы ведете себя так эгоистично? 16 ответов, это не тысяча, с вашими руками ничего не сделается, не так ли? Если уж не знаете, как вежливо и нейтрально написать отказ, обратитесь к вашим HR, они вам подскажут.
                        –7
                        > Так почему же вы ведете себя так эгоистично? 16 ответов, это не тысяча, с вашими руками ничего не сделается, не так ли?

                        Уговорили, больше вообще студентов брать не буду. Никто не заставляет, и эгоистом не прослыву.
                          +5
                          Я, пожалуй, поддержу werklop, несмотря на то, что он мог бы и не переходить на личности. Ожидать от команды стажировок 1000 уникальных ответов было бы странно, а вот если каждый ментор ответит своим 16 студентам, то проблема будет решена. Мне кажется, что даже пара фраз, но уникальных, будет лучше, чем шаблонный ответ.
                          • UFO just landed and posted this here
                              +2
                              Если фидбек дали нормальный, а человек оскорбился, то это только подтверждает правильность решения — хорошо, что не взяли, как с таким вообще работать? С мнением «фидбек давать не нужно, потому что и так все понятно» я в корне не согласен. Фидбек давать нужно всегда, даже когда все очевидно. От пары слов в духе «нужно подтянуть тут и там, встретимся через год» никому хуже не станет. А еще нормально фидбек просить, если его не дали.
                              • UFO just landed and posted this here
                              0

                              Пара уникальных фраз действительно может ощущаться приятнее, но я не представляю, как можно дать конструктивную обратную связь в оффлайн-режиме одним письмом за пару фраз. Это, мне кажется, как минимум совсем отдельный скилл.


                              А неконструктивная всё равно наверняка сведётся к "конкурс большой, кто-то выполнил задание лучше, чем вы, подтяните качество программирования".

                      0
                      или на kotlin ;-)
                        +1
                        По забавному совпадению меня занесло сюда, так как я тоже сделал это задание и меня пригласили на собеседование. Всего, к слову, (по моим наблюдениям) на него отобрали 2^3 — 2^2 человека. Я написал на… Kotlin. (Неожиданно, правда?) И считаю, что мой код достаточно структурирован, а ещё там везде есть комментарии и тесты (а про них было указано отдельно). Ещё проблема автора в том, что на стажировки отбирают только студентов, а автор до 1 сентября им не является. В данном коде очевидна проблема нерасширяемости, как тут упоминалось, а также хардкодинга и прочих STUPID вещей. Ну и всё же, нужно понимать, что если человек должен работать на Kotlin, уметь работать со сборщиками и тестами, то идеальное знание C++/Pascal/Mash/etc. не даст ему никакого веса, так как у конкурентов есть необходимое знание.
                      • UFO just landed and posted this here
                          –4

                          В посте я решил разобрать задачу на стажировку, а не подать апелляцию в JB, так что было бы правильно указывать такие моменты без обобщения :)

                            0
                            хотя возможно если бы ты потратил 7 дней, а не час (ну или день), то можно было бы реализовать UI (вообще странное предположение, что в отладчике не нужен UI, с учетом того, что без него реально отлаживать приложение 80-ти процентам людей невозможно, а критерии оценки решений не прозрачны)
                            +14
                            Если автор и в резюме написал о себе «опыт разработки 8-9 лет, Senior Mash Developer», то после слов «только что выпустился из 11 класса» никто дальше читать не станет и все бумажки отправятся в мусорку. Бизнесу важен реальный опыт, подтвержденный документально, а не воображаемые успехи.
                              –8

                              Мое резюме вы не видели, так что не стоит его комментировать.

                                +10
                                Моя цель — не прокомментировать ваше невидимое резюме, а дать вам понять, что могло пойти не так.
                                Несоответствия между заявленным опытом и остальными данными — это один из самых жестких фильтров.
                                +15

                                Так 8 или 9?
                                Мне 27. Выходит я математик с 22 летним опытом. Математику я всегда любил.

                                  0

                                  Возможно я дурак или что-то не понимаю. Но вроде так и есть. Меня никто ничему в области программирования, к сожалению, не было учил и я учился сам. И вот я понять не могу, если в резюме указывают образование уровень которого в большинстве случаев печален, почему я не могу указать суммарный опыт в области? Лекция онлайн иногда лучше чем лекция офлайн. Я же не скрываю что 5 лет в моей жизни опыта это самообразование а не работа. Ну колупал я таблицы прерываний, изучал структуры данных, ассемблер и кеши, почему если я считаю это важным для себя и вообшем для карьеры не должен указывать это в резюме?

                                    +10

                                    Потому что изучение чего-то это не опыт. Программист не код пишет, он решает проблемы бизнеса. А каким образом это он делает уже мало кого волнует.


                                    Можно 10 лет изучать компьютер сайнс. Прийти на первую работу и во время аварии в пятницу вечером заплакать и убежать к маме.


                                    Можно указывать что вы знаете ассемблер на определенном уровне. Показать проекты.
                                    Но говорить что у вас 5 лет опыта работы на нем некорректно.


                                    Так что опыт это не только компьютерные навыки. Это так же навыки социальные, умение работать в команде, быть стрессоустойчивым.


                                    Вот эти все софтскилы сложно проверить на собеседовании. Поэтому проверяют хардскилы. Отсюда создаётся впечатление, что для работы достаточно одних лишь знаний.


                                    Видете ли в чем разница. Когда вы занимаетесь самообразованием, вы решаете свои проблемы. Если у вас что-то не получается, вы можете просто забить на это.
                                    На работе нужно решать проблемы чужие. Их игнорировать не получится.

                                      0

                                      Ну так в резюме и пишут опыт работы в годах. Но тут то речь о стажировке и первом потенциальном месте работы, так что можно писать, что угодно (просто для информации).

                                        0

                                        Почему я как человек с опытом не могу пойти на стажировку? Может я хочу направление сменить.

                                          0

                                          Хз, зависит от условий конкретной стажировки. Может и можете. Я просто к тому, что если у человека 0 лет опыта работы, то лучше написать что и как долго он изучал, чем ничего не написать.

                                            +1

                                            Вот именно. Изучал. А не имеет опыт.

                                –7
                                1. JB компания большая. Я честно не верю что туда можно зайти с улицы, каким вы бы одиночкой-программистом не были. Вас могут порекомендовать, вы должны заниматься внешней общественной деятельностью (различные статьи, видеоблог и тд), учиться например в ИТМО и быть олимпиадником.
                                2. Для вас это цель получить стажировку и работу, почему-то таких не берут, я думаю потому что ЛПР понимают биологию человека, у вас гормоны прут, а поведение похоже на влюбленного в девушку молодого человека, гормоны меняют сознание, так что вы сейчас прикладываете нереальные усилия, для достижения цели, но никто не знает что будет когда гормоны закончаться, а вот холодное и посредственное отношение обычно ценят, тк работа это монотонная реализация программ и алгоритмов, вдумчивая, неторопливая, но к дедлайну.
                                3. Я сам отправлял резюме в JB, мне c hr удалось поговорить, как мне сказали, что та вакансия, которая мне интересна, увы уже занята, а на другое я не согласился. Мне интересна тема embedded, gdb, отладчиков и поддержка микроконтроллеров в idea, тк я кучу времени потратил на написание прошивок или работе с embedded device.
                                Я сейчас сам visual studio и спец плагин покупаю более 100$, ну и в прошлом году купил лицензию на все продукты JB, тк и под верхи пописываю, хочется только за что-то одно платить, и не пользоваться кучей софта, и мультиплатформенность хочу, толковой ide под mac с поддержкой embedded нет.
                                Ну так вот, к чему я, начните решать какую-то проблему, которая есть в продуктах JB, пилить фичу и выкладывайте на гит, вас заменят.
                                  +7
                                  Ребята, не минусите пожалуйста, у меня и так не важно кармой, поставьте несколько плюсов. Мое мнение редко кому нравиться, но оно мое, а зарабатывать плюсы мне приходится работой, качественными техническими комментариями.

                                  Если вы с чем-то не согласны, напишите лучше почему.
                                    +6
                                    Плюсик то я поставил, но почему заминусили мне видится очевидным;
                                    1. Неграмотная формулировка мыслей, в простонародии — «поток сознания»
                                    2. Собственно, основная причина — второй пункт из вашего комментария, его явно стоило бы опустить, не стоит так судить не зная ТСа лично, ну и в JB вполне себе работают люди без диплома ИТМО, мерж реквестов в Линукс и тысяч звёздочек на гитхабе.
                                      +2

                                      Согласен с EvgeniiR.
                                      Поставил вам "Плюсик" авансом.
                                      Очень вам рекомендую аккуратнее выражать свое субъективное мнение. Как мне кажется, проблема в том, что вы зачастую высказываете его довольно безаппеляционно, и, вероятно, как в данном случае, без глубокого понимания вопроса.

                                  • UFO just landed and posted this here
                                      +1
                                      У меня преподаватель по математике в вузе был таким-же. Позже, когда сам стал аспирантом, и несколько раз вел занятия, был «тираном». У меня позиция была такая, если вы дошли до сюда, пришли учиться, то либо учитесь как того требую, либо не траттье в пустую мое время. Человек, особенно молодой аспирант, которому не все равно, просто не может вести себя по другому.
                                      Взрослые коллеги же, как правило, посредственностям дают возможность получить зачет. Хотя все от человека зависит, моя сестра, преподаватель высшей математики, старше меня на 10 лет, до сих пор максимально требовательна. Ее из деканата уже просить начинают, а она, не ставит, пока не сдаст сам.
                                        +3
                                        > Сравнивал, чтобы не копировали домашки друг у друга

                                        Но как? Если 40 человек с примерно одинаковым опытом, примерно одинаковыми знаниями и одинаковым заданием, то они выдадут одинаковый результат с огромной вероятностью.

                                        > Потом очень строго проверял.
                                        > Оглянитесь, возможно и рядом с вами есть будущие звезды

                                        Человек с крутыми скиллами != хороший препод. Хорошо, что этот человек нашёл себя, но не более того.
                                          +1
                                          Да и не ставить зачёт из-за несоблюдения стайлгайда – тоже весьма странно.
                                            +3

                                            Если styleguide и такое требование озвучены заранее и есть автоматическая утилита для проверки и/или переформатирования (вроде pycodestyle или clang-format) то, мне кажется, вполне разумное требование.

                                            +3
                                            > Но как? Если 40 человек с примерно одинаковым опытом, примерно одинаковыми знаниями и одинаковым заданием, то они выдадут одинаковый результат с огромной вероятностью.
                                            Не выдадут, при всем желании. Каждый человек мыслит по-своему.

                                            Даже два алгоритмически одинаковых решения будут отличаться как минимум структурой кода, порядком команд, названием и типом переменных.
                                            Преподавателю будет ясно видно, кто копипастил, а кто сам делал.

                                            Даже простейшие задачи можно решить разными способами, использовать для этого разные алгоритмы и структуры данных — не все гении, не все будут сплошняком гнать оптимальные решения.

                                            Кто-то будет хранить списки в массиве, кто-то напишет для этого свой класс, кто-то подключит библиотечный.

                                            Декомпозиция задачи у каждого будет разная — кто-то будет писать длинные методы, как тут, кто-то разобьет задачу на подзадачи, и раскидает по своим классам, а кто-то разобьет вообще на простейшие операции, и выстроит целое дерево классов, интерфейсов, и прочего.

                                            Кто-то вынесет настройки в переменные в начало программы, кто-то в константы, кто-то захардкодит прямо в операциях, кто-то конфиг сформирует, а кто-то целый интерактивный конфигуратор сочинит.

                                            Ну и по порядку операций — да, часть операций так или иначе должна идти в строгой последовательности, для решения задачи.
                                            Но никто не мешает раскидать необязательные операции как угодно по коду, а обязательные заменить целой группой других операций, в результате делающих то же самое.

                                            Если человек понимает что он делает — он может скопипастить и видоизменить код так, как будто сам делал.
                                            Но это модификация алгоритма с сохранением работоспособности, работа по сложности схожа с написанием такого же кода с нуля, и даже сложнее, т.к. помимо того, чтобы разобраться в алгоритме решения, человек еще должен будет разобраться во всех хитросплетениях чужого кода и отдебажить его несколько раз, прежде чем получит достаточно разный код, чтобы у преподавателя не щелкнуло в голове «ба! да это же тот же самый код Иванова, только переменные переименованы, и вот эти два оператора местами переставлены».
                                            Такому студенту проще и быстрее самому с нуля накидать свое решение.
                                            И хотя по-началу пару подобных авантюр он предпринять может, но до него очень быстро дойдет, что он тратит больше времени и сил на исправление чужих косяков, чем если бы сделал все с нуля сам.
                                            • UFO just landed and posted this here
                                            +4
                                            А в каком месте в Паскале «удобная реализация строк»?
                                              +2
                                              Например по сравнению с Си?
                                                0
                                                Например по сравнению с Джавой?
                                              0
                                              Такой вопрос. А кто в СПБ кроме ДЖетБраенса и ВК — еще является желанным местом для стажировки?
                                                0

                                                Ещё как минимум Яндекс, Odnoklassniki.

                                                  0
                                                  Раньше Моторола была. Что там у них сейчас со стажировками — не знаю.
                                                    0

                                                    На месте Motorola сейчас Nokia. Студентов набирают время от времени. Не знаю, набрали ли уже нужное количество в этот раз, но в середине мая людей искали.

                                                • UFO just landed and posted this here
                                                    +3
                                                    Язык и компилятор для простейшей стековой машины действительно у него есть — github.com/RoPi0n/mash-lang
                                                    Реализация вполне соответствует школьному уровню (но в 2019 году при наличии интернета можно и поинтереснее/качественнее сделать). Правильные студенты на уровне курсовых делают гораздо лучше.
                                                    А вот дальше, да, не совсем адекватная самооценка.
                                                    • UFO just landed and posted this here
                                                        +1
                                                        Простите, а в каком месте я опровергал ваши слова?
                                                          +1
                                                          А разве «старший разработчик» в его профиле — это не самоирония?
                                                      +17
                                                      Я прочитал статью, а также изучил сайт вашего языка.

                                                      У меня есть для вас совет. Он относится к тому как писать, но не программы, а тексты.
                                                      Вместо оценочных суждений давайте людям факты.
                                                      В IT, по моим наблюдениям, работают люди, которым тяжело пустить в глаза пыль низкопробными «маркетинговыми» фразами.

                                                      «На мой взгляд у меня имеется неплохое резюме с кучей ачивок и хороший скилл, который я день за днем совершенствую последние 8-9 лет

                                                      , а я на данный момент только выпустился из 11-го и сдаю один за другим экзамены.»
                                                      — Почему резюме хорошее? Что говорит о том что у вас хороший скилл? Какие конкретно ачивки?
                                                      И да, как уже писали выше, когда ты видишь фразу про 8-9 лет, то ты воспринимаешь это как-то странно в статье про стажировку.
                                                      Что мешало написать: «К концу 11 класса я запилил свой небольшой язык и компилятор.»? Или еще что-то чем вы гордитесь. Это бы сказало читателю о вашем опыте больше, чем «резюме, ачивки, скилл и 9 лет некоммерческого программирования». И, лично у меня, создало бы хорошее впечатление.

                                                      Тоже самое касается вашего сайта:

                                                      «Быстрый! Производителен и поддерживает многопоточность “из коробки”.» — быстрый насколько? По сравнению с чем? В каких задачах? Как вы вообще это узнали?
                                                      «Мой язык дернул C++ в таких-то тестах, вот код» — это скажет мне о том, что язык быстрый, намного лучше любых прилагательных.

                                                      IT — это не только алгоритмы, паттерны, фреймворки и т.д. IT это люди, и умение работать с ними. Стоит уделить внимание этой области. Этот текст, как вы могли заметить по отзывам выше, не создал о вас впечатление «человека, с которым будет комфортно работать». Код — тоже, по нему уже сказали комментаторы выше.

                                                        +3
                                                        Если уж ты собирался в JB, то похоже нужно было писать на java
                                                          +1
                                                          Выражу свое мнение, как человек который был в схожей ситуации в 2014 году, сразу после школы я решил покорять столицу. Программировать я начал немного позже вашего примерно 12-13 лет, а первый нормальный коммерческий(копейки но все же) опыт имел в 15 лет. В 17 лет я имел 2 долгоиграющих проекта над которыми работал(php, C++).

                                                          Ошибки:
                                                          Я тоже говорил что у меня 5 лет опыта в свои 18 лет, неа не прокатило, я достиг хоть какого то фидбека на мои резюме когда указал что имею 2.5 года опыта на фриленсе и только когда подкрепил это дело контактами людей на которых работал.

                                                          И у меня был свой любимый ЯП, он мне нравился, но любое тестовое задание нужно делать на языке на котором\над которыми предполагается работа — если это в ваших силах, ваше задание примут на любом, но когда идет выбор из двух примерно одинаковых кандидатов, выберут человека с релетивным опытом, а зачем рисковать?

                                                          Опять же про тоже корыто с паскалем, довольно много людей которые желают найти хорошую работу, всегда видоизменяют свое резюме так, чтобы в нем можно было разглядеть ключевые слова из вакансии, а не релетивный опыт вообще сносят, даже если его достаточно много, так что 2 года на java, 2 года на c++ и 2 года на паскале будут звучать убедительней чем каких то фантомных много лет.

                                                          Все остальное время мне приходилось бегать за работодателем, а не наоборот, пару раз я пытался кидаться тем что у меня большой опыт. В итоге, находил маленькие компании, которым было фиолетово на уровень образования(сейчас с этим немного проще), я звонил им, рассказывал почему мог бы подойти им, спустя полтора месяца, я смог составить хоть какое то адекватное резюме и наконец попасть на работу.

                                                          Также, для больших компаний важно ваше умение работать в команде и нет это не комитить в один репозиторий, а реальное адекватное отношение и понимание людей которые будут сидеть за соседним столом/кабинетом/кубиком. И в сопроводительном письме как то осветить это, иначе HR придумает свой вариант.

                                                          Так что, если вам нужна работа сейчас, будьте готовы подстраиваться. Что печально, даже на втором месте работы мне приходилось выпрыгивать из штанов на интервью в компанию которая мне нравилась (пришлось пройти несколько интервью с разницей в неделю, просто потому что у друго кандидата был бакалавр, хоть и меньший опыт работы).

                                                          Оба поиска работы немного меня огорчили и подкосили веру в себя как в программиста, но сейчас я вам пишу из солнечной Калифорнии, так что главное не откладывать ничего на следующий год, стажировки это далеко не единственный путь.
                                                            0
                                                            Поиск правильной работы вообще непростая задача, именно в ИТ, несмотря на острую нехватку специалистов. Где-то хватают тебя, где-то бегаешь ты…
                                                            0

                                                            Как говорили выше: если вы найдете ментора который давал задание и спросите напрямую, фидбек вы скорее всего получите.


                                                            Кроме структуры и расширяемости решения в целом(писали выше), ваш английский заментно хромает, а это тоже сказывается на качестве кода / быстром поике информации(вы скорее всего плохо "сканируете" условые issue на предмет того, что вас интересует). Это тоже могло повлиять на выбор стажера.

                                                              +8
                                                              Здравствуйте! Спасибо за публикацию, нам всегда важна обратная связь.

                                                              Немного поясним, как выглядит все с нашей стороны. Этим летом заявок на стажировки очень много, около 1000. Поэтому предоставить обратную связь каждому после выполнения тестового задания действительно тяжело.

                                                              Сейчас вы можете написать нам на internship@jetbrains.com, мы обязательно попросим ментора дать комментарии по вашему решению.

                                                              Мы планируем добавить отображение фидбека, который смогут получать студенты, в более зрелой версии нашего веб-приложения, сейчас это и правда Beta.

                                                              Вы совершенно верно заметили, что заявки на стажировки могут подавать только студенты. На этапе регистрации в веб-приложении мы доверяем вам. Позже, в случае прохождения всех этапов отбора, мы обязательно просим предоставить справку из вуза о том, что вы являетесь студентом на данный момент.

                                                              Best,
                                                              JetBrains Education Team
                                                                +9
                                                                Я выполнил тестовое задание (и как мне кажется хорошо)

                                                                Давайте посмотрим.


                                                                При оценке работы будет оцениваться:

                                                                Качество исходного кода и наличие тестов;

                                                                Когда говорят о «наличии тестов», то чаще всего подразумевают unit tests.
                                                                В Java для это есть фреймворк JUnit, в Delphi — DUnit, в FreePascal — fpcunit.
                                                                У вас же просто батник, запускающий примеры из папки tests, это несколько не то.


                                                                Я бы не сказал, что оно сложное, скорее наоборот. Оно не требует каких-либо глубоких знаний теории построения трансляторов и крутого скилла.
                                                                Несмотря на то, что JB дают аж целых 7 дней, по времени у меня в сумме ушло около часа, а проект получился примерно в 500 строк кода.

                                                                Некоторое время на изучение теории выделить всё же стоит.
                                                                При парсинге у вас модифицируется входная строка, это явный признак того, что вы делаете что-то не так.
                                                                Стоит только добавить строковые литералы, как вызовы StringReplace() станут очень некстати. Сейчас же они просто лишние.


                                                                При этом вы не запоминаете позицию, на которой закончили в предыдущий вызов GetToken() и каждый раз начинаете обрабатывать строку с начала, хотя это совершенно избыточно.
                                                                Более того, GetToken() и FillSpaces() вообще не имело смысла писать. Обе функции имеют готовые реализации в FPC — ExtractWord() и StringOfChar() соответственно.


                                                                В Java и вовсе можно было взять StringTokenizer и получить лексер бесплатно.
                                                                К слову о правильности выбора языка.


                                                                type
                                                                 TGuuOp = class
                                                                   public
                                                                
                                                                     OpType         : TGuuToken;
                                                                ...
                                                                     OpReg          : Pointer;

                                                                В классах у вас все поля публичные, что не очень хорошая практика.
                                                                Тем более что декларация property это всего лишь одна дополнительня строка.


                                                                Поле OpReg используется для хранения ссылок на экземпляры классов TGuuOp и TGuuVar, но при этом почему-то имеет тип Pointer.
                                                                А так как TGuuOp и TGuuVar не имеют ничего общего, то возникает вопрос, почему ссылки на них хранятся в одном и том же поле.


                                                                OpType := TGuuToken(AnsiIndexStr(s, GuuToken));

                                                                Приводить целое число к перечислимому типу — плохая практика, по возможности стоит этого избегать.
                                                                Функция AnsiIndexStr() может вернуть в качестве значения -1, в этом случае в OpType вместо значения будет лежать мусор, не представимый типом TGuuToken. В case можно было использовать результат AnsiIndexStr() напрямую.


                                                                После того, как было написано объявление класса — я решил, что наиболее компактным и красивым решением было бы дописать парсер и небольшой синтаксический анализ в его конструкторе, что я дальше и сделал:

                                                                В конструкторе лучше ограничиться минимальной инициализации, а основную работу делать в отдельном, явно вызываемом методе.
                                                                Так будет проще отлаживать, у вас всегда будет корректно созданный объект, хранящий текущее состояние.


                                                                type
                                                                 TGuuVar = class
                                                                   public
                                                                     gvName: string;
                                                                     gvVal: variant;
                                                                     constructor Create(VarName: string);
                                                                 end;

                                                                Единственный поддерживаемый тип переменных — целое число, но вы зачем-то храните значение в поле типа Variant.


                                                                var
                                                                 GuuVars: TList;

                                                                Переменные вы храните в линейном списке, это потенциальные проблемы с производительностью на пустом месте. Хеш-таблица с именем переменной в качестве ключа здесь подошла бы больше.


                                                                Т.к. тестовое задание призвано показать качество кода, который вы можете порождать, то это имеет значение даже для интерпретатора игрушечного языка.


                                                                Время жизни объектов у вас не защищено блоками try ... finally, на сколь-нибудь сложном коде это черевато утечками памяти.


                                                                Это то, что бросилось в глаза при беглом осмотре.

                                                                  +1
                                                                  Спасибо за развернутый комментарий. В нем есть хорошие замечания, но стоит сказать пару слов про OpReg: Pointer и TList для хранения переменных.

                                                                  Доступ к классам в Object Pascal осуществляется по указателю на экземпляр класса.
                                                                  Конструктор класса возвращает указатель на созданный экземпляр.
                                                                  Таким образом мы можем передавать доступ к экземплярам классов по через любой тип указателя (Pointer).

                                                                  Использовать TList для хранения переменных — это действительно жутко не производительно.
                                                                  Использовать хеш-таблицу — гораздо более лучший вариант.

                                                                  Я реализовал более производительный вариант, в котором в процессе интерпретации не нужно обращаться к TList или хеш-таблице за переменными.
                                                                  Указатели на экземпляры классов переменных помещаются в OpReg в процессе анализа кода и затем обращение к переменным идет по уже готовым указателям в этом буфере.
                                                                  +8
                                                                  Добавляю комментарий от руководителя проекта Яна Жуланова:

                                                                  «Доброго времени суток! Я ментор проекта „Отладчик корутин для Kotlin“. К сожалению, мне пришлось отклонить вашу заявку на стажировку по трём причинам.

                                                                  Во-первых, как уже сказала моя коллега Вера, по текущим правилам мы можем принять только людей со статусом „студент университета“. Это формальное требование, с этим прямо сейчас ничего сделать не получится.

                                                                  Во-вторых, реализация других кандидатов оказалась более качественной. Как вы верно заметили, задание было несложным и не требовало использования сложных алгоритмов. Вы выбрали ООП-язык (Object Pascal), поэтому главным образом я смотрел на правильность применения этой парадигмы. Вот, что хотелось бы улучшить:
                                                                  • В главной функции слишком много кода – здесь и парсинг аргументов командной строки, и чтение программы, и исполнение кода, и команды дебаггера. Как минимум просится разделение на отдельные методы, а то и классы.
                                                                  • Требование „простота расширения функциональности“ не удалось выполнить. Все операторы и команды дебаггера обрабатывают огромные case. В последнем случае – прямо внутри main-функции.
                                                                  • Большое количество мутаций в коде. Как уже написали комментаторы выше, самое проблемное место – парсер, сделанный через Delete, Trim, StringReplace.
                                                                  • Требование „наличие тестов“ также не выполнено. В проекте нет тестов.

                                                                  Последняя причина – выбранный язык программирования. Задания действительно можно было отправлять на любом языке из TIOBE Top 50, и по этому пункту не было дискриминации. Однако в описании проекта на стажировку неспроста сказано про необходимость „уметь программировать на Java, Kotlin или Scala“. В случае, когда код тестового задания написан на языке, отличном от этих трёх, единственная возможность проверить, умеет ли кандидат программировать на Java – посмотреть его профиль на Github. У вас в Github на Java существует единственный проект (https://github.com/RoPi0n/Tiny-RCon-client), по которому очень сложно оценить знания Java.

                                                                  Не отчаивайтесь. Я надеюсь, что вы поступите в университет и углубите свои знания в программировании.

                                                                  Вы правы, у нас очень захромал фидбек – новый сайт стажировок только-только открылся и работает в бета-стадии. Мы обязательно что-нибудь придумаем с отзывами.»
                                                                    0
                                                                    В статью добавить бы RoPi0n
                                                                    –2
                                                                    Ого! Вот это да! Вы и есть Mash? Жалко конечно что JetBrains не читают Хабр (появились только когда статья тут вышла), ибо если бы они знали кто Вы, Вас бы взяли авансом ни смотря на очевидные досадные косяки, потому-что одно дело когда человек пишет свой ЯП в 30 лет, а другое дело когда он так юн. Ну а про выбор Паскаля уже писали выше, от себя просто добавлю что это хорошо конечно иметь любимый язык, но не стоит забывать, что наши образовательные стандарты сильно устарели и поэтому к сожалению уже после окончания школы приходится переходить на что-то другое, а не насиловать труп, а уж когда Вы сами признались что пишите и на Java в том числе, то выполнять на Паскале задание в JetBrains — это умышленно выстрелить себе в рот, что и было сделано. Но на ошибках учатся, из этой ситуации я бы вынес урок в том, что Паскаль уже практически вымер, хотим мы этого или нет. А если уж так хочется отсутствия фигурных скобок и отступов, то Питон отлично подойдет. Но Java лучше :D
                                                                      +1

                                                                      Deleted. Комментарий уже опубликовали мои коллеги.

                                                                      Only users with full accounts can post comments. Log in, please.