Pull to refresh
11
0
Send message
Но для правильного руководителя работа с фобиями — это настоящий клад, потому что открывается почти прямой доступ к мотивациям человека.
Потому что можно «расширить и углубить»(с)
Я больше склоняюсь к мысли, что тут работает простое и понятное желание получить лучших из лучших (а платить им… в лучшем случае как среднячкам). Вот только очень уж часто работодатели забывают что это всё-же СОбеседовавшие, которое предполагает СОтрудничество. Из чего следует, что потенциального работника нужно таки заинтересовать, а не продемонстрировать/выпячивать своё превосходство.
«Послушаешь вопросы на собеседованиях — не иначе как гениев ищут. Посмотришь код проекта — и куда только всех гениев дели?»((с)тырено)

Людей следует искать под конкретный проект, конкретную задачу а не «сферического программиста в вакууме». Соответственно этому нужно и готовить список вопросов. В обсуждаемой статье отсутствует цель найти кого-то определённого, т.к. слишком много направлений затронуто (frontend, backend, менеджмент, архитектура...). К тому же, чтобы обстоятельно ответить хотя бы на половину вопросов, да ещё и с неминуемыми уточнениями по ходу разговора, денька два может уйти влёгкую. Пойдёт-ли на такие расходы фирма и согласится-ли на такое, с позволения сказать, собеседование мало-мальски толковый профи — вопрос, думаю, риторический.

Вот как своеобразная шпаргалка, база «дежурных» вопросов (а скорее — тем для построения своих вопросов) эта статья очень даже полезна, как по мне.
Опять таки из личного опыта.

Мене приходилось отказывать соискателю на основании того, что он не впишеться в команду по чисто психологическим причинам. По знаниям у человека всё было в нужных пределах, но ни в одну из существующих команд его принять нельзя — развалится всё к чертям. Проектов/задач для одиночки на тот момент на горизонте не просматривалось. Так что…

Что же касается нетрадиционной ориентации. Если человек адекватный, не выпячивает свои особености — я бы посоветовал ему не распространяться на эту тему. Т.к. иначе начнётся де-факто травля, хотя де-юре всё «чисто». Ежели человек любит эпатаж и/или специально привлекает внимание к себе — найду формальный повод, чтобы отказать, т.к. мне гораздо важнее работающая команда.
Мне на собеседованиях как-то не приходилось сталкиваться «со следами похмелья». Однако же я вдоволь насмотрелся на таких вот «раздолбаев». Такой себе — «синдром студента» (не в обиду будь сказано):

  • скорее спринтер, нежели марафонец (ритм на длинной дистанции не держит совершенно);
  • «дедлайн == сессия», только при её приближении начинает хоть как-то напрягаться;
  • авось «на сессии» выкручусь («халява приди»);
  • ну ладно, на крайняк пересдача есть (ПМ подвинет дедлайн);
  • «И так сойдёт»(с);
  • изумительной скорости «генератор отмазок»;
  • ...


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

P.S. Вышеупомянутая симптоматика мне встречалась независимо от пола.
Неопрятность в одежде вызывают у меня подозрение, что тут одно из двух:
  • человек ко всему относится как и к одежде;
  • он сконцентрирован на чём-то одном (коде/задаче), а всё остальное — по остаточному принципу.

Достоверный ответ на этот вопрос (а равно и другие спорные) непосредственно на собеседовании получить практически не реально. Посему — используются иные источники информации (github, соц.сети, «знакомые знакомых» и т.д. и т.п.). Ежели прояснить не удалось — добро пожаловать на испытательный срок. В большинстве случаев за пару-тройку недель ответы как-то «автомагически» находятся.
Вопрос, конечно же, не мне был задан. Однако попробую высказаться со своей колокольни

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

При прочих равних конечно же выберу более опрятного (и далее по списку). Впрочем в реальной жизни этих самых «прочих равных» я не встречал. Поэтому вопрос «опрятности» будет далеко не на первом месте при решении вопроса.

… если один из них будет с дипломом какого-нибудь хорошего вуза, а другой — без
Как показывает моя практика (десятки/сотни проведённых собеседований) наличие/отсутствие диплома какого-либо ВУЗа не решает ровным счётом ничего. Решает в первую очередь опыт и, конечно же, самообразование.
Как это было сделано — без понятия. Но этот «фейк» пару лет тому продавался практически во всех магазинах. И да, именно с таким штампиком. Сейчас его, штампик этот, вроде как убрали уже.
и мне, пожалуйста
Более того, добавлять разделы можно не пустыми, а уже с файлами.

А как разрешаются конфликты? Если, к примеру, на нескольких разделах имеются файлы/директории с одинаковым именем, то что и как будет видно/доступно?
Спасибо за ответ.
По поводу быстродействия. А Вы не думали добавить какую-то опцию, которая бы позволяла включать проверку только в dev/test-окружениях?
Интересная работа. Но по прочтении у меня возникла парочка вопросов.
Перекраивание интерфейсов на специальные пользовательские структуры вроде namedtuple вместо **kwargs имело несколько проблем

Если это возможно, расскажите, пожалуйста, подробнее почему в Вашем случае нельзя было использовать замену параметров объектом (см. пример)?

И второй момент. Насколько я понимаю, то проверки происходят runtime. В связи с этим интересно было бы узнать насколько «проседает» быстродействие из-за использования этого инструмента?
Признаться, я в затруднении. Конечно же, со студенческих времён забылось многое (в т.ч. и по «вышке»), но почему-то думалось мне, что сходимость-то я могу обнаружить. Ан-нет. Вы утверждаете, что алгоритм сходится, т.е. имеет предел по времени выполнения и/или количеству итераций. Но, к стыду своему, я не вижу этого. Помогите, пожалуйста, найти ошибку в моих рассуждениях.

Рассуждал же я следующим образом.

Берём код запроса и строчка за строчкой смотрим, что происходит.
смотреть код
WITH RECURSIVE
r AS (
    SELECT array[]::integer[] AS res,min(id) AS min, max(id)-min(id) AS range FROM table1
  UNION ALL
    SELECT res||ARRAY(SELECT id FROM table1 WHERE id IN (SELECT (min+range*random())::int FROM generate_series(1,1000)) AND NOT id=ANY(res)), min, range
    FROM r
    WHERE
      coalesce(array_length(res,1),0)<1000
)
SELECT unnest(res) FROM (
  SELECT res FROM r ORDER BY array_length(res,1) DESC NULLS LAST LIMIT 1
) AS t LIMIT 1000;

  • Рекурсивный запрос — без этого никуда;
  • инициализируем переменные: под хранение результата, минимального и максимального значений а также, для удобства видимо, переменную хранящую диапазон между min-max значениями;
  • union — соединяем инициализацию и поиск значений;
  • собственно рабочий запрос (подробнее о нём ниже);
  • и, конечно-же, «выдача» результата;


Суть алгоритма заключена в рабочем запросе. Т.е. вот в этом
SELECT res||ARRAY(SELECT id FROM table1 WHERE id IN (SELECT (min+range*random())::int FROM generate_series(1,1000)) AND NOT id=ANY(res)), min, range
FROM r
WHERE coalesce(array_length(res,1),0)<1000

Тут сказано следующее:
  1. случайным образом генерируем 1000 id в диапазоне min-max (generate_series(1,1000));
  2. из нужной таблицы (table1) выбираем id при условии, что эти id принадлежат последовательности, созданной на предыдущем этапе и их ещё не выбирали прежде;
  3. найденный результат поместить в переменную res;
  4. повторить, пока в res не наберётся 1000 записей.

Если не ошибаюсь, то на шаге 3 в переменную res не будет помещено ничего (размер массива не изменится) если на шаге 2 ничего не было найдено.

Поскольку мы имеем дело тут с непредсказуемым фактором (random()) то нужно рассматривать два сценария — оптимистический (он же позитивный) и пессимистический (он же негативный).

Оптимистический
На каждом шаге запрос находит минимум одну запись (в пределе — все записи находятся за один проход). Отлично, имеем упомянутые цифры
… версия которая на коротких списках работает приблизительно так же как и предыдущая а вот на списках в 1000 случайных работает в 3 раза быстрее и главное требует на 3 порядка меньше памяти для работы (предыдущая версия требовала на 1000 случайных значений при 0.01 заполнении больше 200MB текущая в 1MB влезает и работает за 150ms)

Пессимистический
Что может пойти не так? Это же всё-таки случайность, random. Он может несколько раз (в пределе — постоянно в течении неограниченного времени) выдавать результат (генерировать id-шки), которых нет в таблице.

Как такое может произойти? Очевидный ответ — лакуны, «дырки» в последовательности id. И это могут быть как дырки «естественного» (нет физически записей с такими-то значениями) или «искусственного» происхождения (дополнительные условия, которые накладываются на строки, подлежащие выборке).

Всё? Не совсем. Запрос не «запоминает» ошибок, промахов. Т.е. вполне возможна ситуация, когда запрос начнёт «ходить кругами» (random будет генерировать повторяющиеся последовательности). В сочетании с вышеупомянутой особенностью это может привести к тому, что использование этого запроса, с точки зрения скорости выполнения, будет хуже решения «в лоб».

Да, вероятность реализации худшего сценария очень низка. Но она не нулевая. При этом с увеличением частоты запросов всё чаще этот худший сценарий будет реализовываться. И мне очень не хочется, чтобы эта «особенность» («фича») всплыла на демонстрации продукта начальству/инвесторам/заказчикам/важному пользователю (нужное подчеркнуть).

Выводы:
  • упомянутый алгоритм не имеет предела по времени выполнения/количеству итераций. Он работает за разумное/приемлемое/ограниченное время только с определённой, хотя и довольно большой, вероятностью;
  • при добавлении ограничения на количество итераций (времени выполнения) запрос не гарантирует возвращение какого-либо результата;
  • запрос не применим, для задач где нужно гарантировать получение результата за ограниченное количество времени, пусть и в ущерб равномерности распределения и/или неповторяемости;
  • запрос не применим для случаев, когда количество записей в таблице меньше требуемого (дополнительными условиями на получаемые строчки этого более чем просто получить);
Любопытное решение. Но обратите, пожалуйста, внимание на вот этот фрагмент
WHERE id=(SELECT (min+range*random())::int) AND NOT id=ANY(res))

или его аналог для оптимизированного случая
WHERE id IN (SELECT (min+range*random())::int FROM generate_series(1,1000)) AND NOT id=ANY(res))

Фактически это генерация id-шек для определённого диапазона (min-max). При упомянутом подходе существует только вероятность, но не гарантия, что полученное значение присутствует в таблице.

Кроме того, в указанном примере отсутствует ограничение на количество итераций и, следовательно, сохраняется риск «зацикливания». Да, добавить ограничение не составляет проблемы. Однако при ограничении количества попыток «угадать ответ» существует не нулевой шанс, что этот запрос ничего не найдёт или найдёт меньше нужного количества записей.

Для некоторого класса задач такой подход оправдан. В моём случае, к сожалению, нет. Я скорее готов смириться с некоторой долей предопределённых и/или повторяющихся результатов, нежели с отсутствием оного результата.
Только что проверил.
SELECT * 
FROM ttbl 
WHERE id not in(1, 3, 10, 89, 99, 22, 24, 25, 28, 30) 
OFFSET (random()* 1000)::int
LIMIT 1

Время выполнения одного запроса 2-14ms. Нужно их 10. При этом нужно учесть, что это только время на выполнения запроса в базе. Но будут ещё накладные расходы на доставку каждого ответа в приложение, обработку, формирование нового запроса…
Спасибо за линк. Возможно в той ветке форума и найдут лучшее решение. На это позволяет надеяться хотя бы тот факт, что один из вариантов решения уже упомянули.
Вот эта версия работает на совершенно любом распределении и работает корректно.
Но на сильно разреженных таблицах конечно не быстрая:

тело запроса
WITH RECURSIVE
range AS (
  SELECT min(id) AS min, max(id) AS max FROM table1
), 
r AS (
    SELECT array[]::integer[] AS res
  UNION ALL
    SELECT CASE WHEN (id IS NULL) THEN res ELSE res||id END 
    FROM (SELECT (min + (max - min) * random())::int AS rnd,res FROM r JOIN range ON true) AS _r
    LEFT JOIN table1 ON id=rnd AND id <> all(res) 
    WHERE 
      id IS NULL OR coalesce(array_length(res,1),0)<=10 
) 
SELECT unnest(res) FROM (
  SELECT res FROM r ORDER BY array_length(res,1) DESC NULLS LAST LIMIT 1
) AS t;

Вот только приведённое решение нестабильно — зацикливается поскольку. Только что проверил на таблице с 4 строчками указав выбрать 3.
Но само направление поиска довольно интересное. Вероятно если вдумчиво «раскурить» приведённый запрос проблему с зацикливанием удастся обойти.
Спасибо, что указали на этот момент. Действительно при больших «дырках» в распределении значений ID время от времени будет возвращаться одна и та же последовательность. И хотя сейчас для моего случая это не критично, но вскоре нужно будет как-то этот вопрос решить.
Пока что хороших идей по этому поводу у меня нет. Из остальных — разве что разделить «primary key» и «sort key». После чего хранимыми процедурами поддерживать приемлемый уровень разрежённости «sort key».
Если бы дело было только в «primary key» — фиг бы с ним. Но нужно учитывать ещё и условия на остальных столбцах. А это уже дублирование логики приложения в СУБД, со всеми последствиями такого шага.
Да, конечно.

План запроса
Nested Loop  (cost=2.42..95.38 rows=11 width=5) (actual time=4.633..11.737 rows=5 loops=1)
  Buffers: shared hit=26 read=23
  CTE r
    ->  Recursive Union  (cost=0.29..2.42 rows=11 width=48) (actual time=4.184..10.575 rows=5 loops=1)
          Buffers: shared hit=10 read=19
          CTE b
            ->  Result  (cost=0.28..0.29 rows=1 width=0) (actual time=2.340..2.340 rows=1 loops=1)
                  Buffers: shared hit=2 read=9
                  InitPlan 1 (returns $1)
                    ->  Limit  (cost=0.19..0.24 rows=1 width=4) (actual time=1.121..1.122 rows=1 loops=1)
                          Buffers: shared hit=2 read=4
                          ->  Index Scan Backward using ttbl_pkey on ttbl t  (cost=0.00..47048.93 rows=1000990 width=4) (actual time=0.830..1.116 rows=5 loops=1)
                                Filter: (is_active AND (id <> ALL ('{1,3,10,89,99,22,24,25,28,30}'::integer[])))
                                Buffers: shared hit=2 read=4
                  InitPlan 2 (returns $2)
                    ->  Limit  (cost=0.00..0.05 rows=1 width=4) (actual time=1.207..1.207 rows=1 loops=1)
                          Buffers: shared read=5
                          ->  Index Scan using ttbl_pkey on ttbl t  (cost=0.00..49551.43 rows=1000990 width=4) (actual time=1.205..1.205 rows=1 loops=1)
                                Index Cond: (id IS NOT NULL)
                                Filter: (is_active AND (id <> ALL ('{1,3,10,89,99,22,24,25,28,30}'::integer[])))
                                Buffers: shared read=5
          ->  Subquery Scan on "*SELECT* 1"  (cost=0.00..0.18 rows=1 width=12) (actual time=4.179..4.181 rows=1 loops=1)
                Buffers: shared hit=3 read=15
                ->  Limit  (cost=0.00..0.17 rows=1 width=12) (actual time=4.178..4.179 rows=1 loops=1)
                      Buffers: shared hit=3 read=15
                      ->  Nested Loop  (cost=0.00..55313.90 rows=333663 width=12) (actual time=4.175..4.175 rows=1 loops=1)
                            Join Filter: (t.id >= (b.min + ((((b.max - b.min))::double precision * random()))::integer))
                            Buffers: shared hit=3 read=15
                            ->  CTE Scan on b  (cost=0.00..0.02 rows=1 width=8) (actual time=2.342..2.342 rows=1 loops=1)
                                  Buffers: shared hit=2 read=9
                            ->  Seq Scan on ttbl t  (cost=0.00..26952.50 rows=1000990 width=4) (actual time=0.015..0.907 rows=1368 loops=1)
                                  Filter: (is_active AND (id <> ALL ('{1,3,10,89,99,22,24,25,28,30}'::integer[])))
                                  Buffers: shared hit=1 read=6
          ->  Limit  (cost=0.00..0.17 rows=1 width=48) (actual time=1.273..1.274 rows=1 loops=5)
                Buffers: shared hit=7 read=4
                ->  Nested Loop  (cost=0.00..173819.35 rows=1000980 width=48) (actual time=1.272..1.272 rows=1 loops=5)
                      Join Filter: ((t.id <> ALL (r.a)) AND (t.id > (r.min + ((((r.max - r.min))::double precision * random()))::integer)))
                      Buffers: shared hit=7 read=4
                      ->  WorkTable Scan on r  (cost=0.00..0.25 rows=3 width=44) (actual time=0.005..0.005 rows=1 loops=5)
                            Filter: ((n + 1) < 5)
                      ->  Materialize  (cost=0.00..35868.45 rows=1000990 width=4) (actual time=0.007..0.740 rows=1012 loops=4)
                            Buffers: shared hit=7 read=4
                            ->  Seq Scan on ttbl t  (cost=0.00..26952.50 rows=1000990 width=4) (actual time=0.019..1.442 rows=2335 loops=1)
                                  Filter: (is_active AND (id <> ALL ('{1,3,10,89,99,22,24,25,28,30}'::integer[])))
                                  Buffers: shared hit=7 read=4
  ->  CTE Scan on r  (cost=0.00..0.22 rows=11 width=4) (actual time=4.189..10.591 rows=5 loops=1)
        Buffers: shared hit=10 read=19
  ->  Index Scan using ttbl_pkey on ttbl t  (cost=0.00..8.42 rows=1 width=5) (actual time=0.221..0.223 rows=1 loops=5)
        Index Cond: (id = r.id)
        Buffers: shared hit=16 read=4
Total runtime: 12.185 ms


Правда план запроса под стать запросу. Я пытался его «раскурить», но потом решил что проще будет проверить экспериментально.

Information

Rating
Does not participate
Registered
Activity