Местоположение
Россия, Калининградская обл., Калининград (Кенигсберг)

Достижения

Все достижения (2)

Наибольший вклад в теги

Все теги (25)

Лучшие ответы пользователя

Все ответы (27)
  • На собеседовании сказали, что не все функции - замыкания. Так ли это?

    @Arlekcangp
    Разработчик, Лид, Архитектор ПО
    Похоже вас поймали на том что в js кто-то решил переопределить термин "замыкание"... Т к javascript далеко не первый язык, в котором есть нечто, претендующее называться термином "замыкание", то следует в первую очередь рассмотреть общее определение. Возьмём его из википедии:

    Замыкание (англ. closure) в программировании — функция первого класса, в теле которой присутствуют ссылки на переменные, объявленные вне тела этой функции в окружающем коде и не являющиеся её параметрами. Говоря другим языком, замыкание — функция, которая ссылается на свободные переменные в своей области видимости.
    Замыкание, так же как и экземпляр объекта, есть способ представления функциональности и данных, связанных и упакованных вместе.
    Замыкание — это особый вид функции. Она определена в теле другой функции и создаётся каждый раз во время её выполнения. Синтаксически это выглядит как функция, находящаяся целиком в теле другой функции. При этом вложенная внутренняя функция содержит ссылки на локальные переменные внешней функции. Каждый раз при выполнении внешней функции происходит создание нового экземпляра внутренней функции, с новыми ссылками на переменные внешней функции.
    В случае замыкания ссылки на переменные внешней функции действительны внутри вложенной функции до тех пор, пока работает вложенная функция, даже если внешняя функция закончила работу, и переменные вышли из области видимости.[1]
    Замыкание связывает код функции с её лексическим окружением (местом, в котором она определена в коде). Лексические переменные замыкания отличаются от глобальных переменных тем, что они не занимают глобальное пространство имён. От переменных в объектах они отличаются тем, что привязаны к функциям, а не объектам.


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

    In programming languages, a closure, also lexical closure or function closure, is a technique for implementing lexically scoped name binding in a language with first-class functions. Operationally, a closure is a record storing a function[a] together with an environment.[1] The environment is a mapping associating each free variable of the function (variables that are used locally, but defined in an enclosing scope) with the value or reference to which the name was bound when the closure was created.[b] Unlike a plain function, a closure allows the function to access those captured variables through the closure's copies of their values or references, even when the function is invoked outside their scope.


    О! А тут нет никаких "функций, вложенных в функцию" и нет никаких "создающихся каждый раз" Хм... Интересно. Ну за то есть абзац о том как появились замыкания:

    The concept of closures was developed in the 1960s for the mechanical evaluation of expressions in the λ-calculus and was first fully implemented in 1970 as a language feature in the PAL programming language to support lexically scoped first-class functions.[2]

    Peter J. Landin defined the term closure in 1964 as having an environment part and a control part as used by his SECD machine for evaluating expressions.[3] Joel Moses credits Landin with introducing the term closure to refer to a lambda expression whose open bindings (free variables) have been closed by (or bound in) the lexical environment, resulting in a closed expression, or closure.[4][5] This usage was subsequently adopted by Sussman and Steele when they defined Scheme in 1975,[6] a lexically scoped variant of Lisp, and became widespread.


    Ага. Оказывается первоначально они появились в такой не конвенциональной, с сегодняшней точки зрения VM, как SECD... И их создатель прямо говорит, что это код плюс данные. Далее термин применяют к лямбда-выражениям. Ничего не говорится, должны ли эти выражения возвращаться функцией, или достаточно того, что бы они захватывали переменные из своего лексического (или динамического, как в common lisp) окружения. Далее нам сообщают, что некие мистеры, Сасман и Стил, спустя каких то 11 лет добавили это в свой язык Scheme... Ну что же. Посмотрим замыкания в Scheme... А именно откроем книгу Сасмана и соавторов "Структура и интерпретация компьютерных программ" или "SICP" Первый раз (исключая содержание) слово "замыкание" встречается в главе 2 "Поcтроение абстракций c помощью данных"


    Важная идея в работе с составными данными — понятие замыкания (closure): клей для
    сочетания объектов данных должен позволять нам склеивать не только элементарные
    объекты данных, но и составные.


    Но, как выяснилось, это не те замыкания... Это замыкание в том смысле, что структуру одного типа (в данном случае пары) могут ссылаться на самих себя. И это то как термин "замыкание" понимают математики. Далее в SICP никаких других определений замыканий нет (несмотря на то, что само их использование конечно же есть) Ну ничего не остаётся как посмотреть что же сегодня называют замыканиями в языке Scheme... Гугл по этому запросу выдал первой ссылкой: https://www.cs.utexas.edu/ftp/garbage/cs345/schint....
    Цитирую:

    Procedures are Closures
    Scheme procedure's aren't really just pieces of code you can execute; they're closures.

    A closure is a procedure that records what environment it was created in. When you call it, that environment is restored before the actual code is executed. This ensures that when a procedure executes, it sees the exact same variable bindings that were visible when it was created--it doesn't just remember variable names in its code, it remembers what storage each name referred to when it was created.

    Since variable bindings are allocated on the heap, not on a stack, this allows procedures to remember binding environments even after the expressions that created those environments have been evaluated. For example, a closure created by a lambda inside a let will remember the let's variable bindings even after we've exited the let. As long as we have a pointer to the procedure (closure), the bindings it refers to are guaranteed to exist. (The garbage collector will not reclaim the procedure's storage, or the storage for the let bindings.)

    Here's an example that may clarify this, and show one way of taking advantage of it.

    Suppose we type the following expression at the Scheme prompt, to be interpreted in a top-level environment:

    Scheme> (let ((count 0))
               (lambda ()
                  (set! count (+ count 1))
                  count)))

    ##

    Evaluating this let expression first creates a binding environment with a binding for count. The initial value of this binding is 0. In this environment, the lambda expression creates a closure. When executed, this procedure will increment the count, and then return its value. (Note that the procedure is not executed yet, however--it's just created.) This procedure, returned by the lambda expression, is also returned as the value of the let expression, because a let returns the value of its last body expression. The read-eval-print loop therefore prints a representation of the (anonymous) procedure.


    Что имеем ? В Scheme - вообще любая функция - это замыкание! Т к она всегда захватывает контекст, даже если там пусто!. Что видим в коде ? Ну с точки зрения Scheme тут конечно вложенная функция, т к let - это на самом деле lambda, но так происходит потому, что в Scheme нет переменных в понимании js. В js же можно считать, что глобальный контекст - результат действия нечто, что эквивалентно let. Захватывает ли любая функция в js этот контекст ? Не знаю. Но точно знаю, что в вашем примере это определенно так - х часть контекста, который захватывается функцией. Кто хочет поспорить - идите спорьте с создателями Scheme (скажите, когда попкорн покупать, я посмотрю как вас пороть будут. Набежало тут вайтишников =)) Кто то может сказать "а вот у нас в js замыкания определяются не так" На что я отвечу: знаете, товарищи, а идите ка вы лесом! Если есть однажды созданный и определенный термин, нужны весьма веские причины что бы менять его определение. Желания левой ноги очередного вайтишника тут не достаточно.
    Ответ написан
    30 комментариев
  • Как повысить свои навыки в построении архитектуры сложных приложений?

    @Arlekcangp
    Разработчик, Лид, Архитектор ПО
    1. Хорошо помогает начать изучение с простых паттернов проектирования. Прежде всего это простые и понятные паттерны типа "стратегия", "команда", "итератор", "шаблонный метод", "посредник", "цепочка обязанностей". Изучив и поняв эти паттерны вы посмотрите на ООП по новому: не как просто структурированный код плюс данные в одном объекте, а именно как задумывалось его создателем - объект самостоятельная единица взаимодействующая с другими такими же посредством сообщений. Причём она является first class также как в функциональном программировании функция. К тому же на указанных паттернах строятся и остальные. Например, фабричный метод - это частный случай шаблонного метода. Так постепенно придет понимание куда и зачем применять различные паттерны.
    2. Когда решаете какую-либо задачу, думайте о нескольких вариантах архитектуры для её решения. Далее старайтесь выбирать
    вариант не на основе личных предпочтений или предыдущего опыта (не важно, положительного или отрицательного), а на основе анализа, какой из вариантов здесь реально потребуется с точки зрения дальнейшего развития проекта. Предыдущий опыт также надо учитывать, но все проекты разные, требования разные, и каждая ситуация может отличаться. Надо смотреть как могут изменяться или расшириться системные и функциональные требования (разумеется, для этого надо быть в контексте этих требований - т е знать их самих, манеру работы с проектом заказчика и т д) Во многих случаях, когда вы не сможете выбрать из-за недостатка информации, это логически подведёт вас задавать заказчику дополнительные вопросы. И через этот итеративный процесс приходит понимание, где и как применять паттерны.
    3. Обратите внимание на паттерны ERP систем (для примера книга "Шаблоны корпоративных приложений" Мартин Фаулер) Особенное внимание надо уделить такому шаблону как инверсия зависимостей. Данный шаблон лично мне помог совершенно по другому взглянуть на ООП (во второй раз, уже после того как я стал применять другие паттерны ООП) Вот здесь https://blog.byndyu.ru/2009/12/blog-post.html очень понятно на мой взгляд описано (язык C# но всё тоже самое будет для любого ОО языка) Кроме того в этом блоге много всего по проектированию и рефакторингу.
    4. Обратите внимание на книгу "Growing Object-Oriented Software, Guided by Tests" Стив Фриман Перевод на русский не гуглится, возможно его и нету. Но книга полезна тем, что в отличие от многих других книг по TDD в ней разбирается не только методика тестирования и написания тестов, но и принцип тест -> код -> рефакторинг. И разбирается на достаточно длинном примере. Из неё вы можете подчерпнуть привычку рефакторить, а не переписывать заново. Причём даже если у вас на проекте цикл другой - например тесты пишутся после функционала, всё равно образ мысли изменится и масштабный рефакторинг не будет вызывать непреодолимого желания выбросить и переписать с нуля.
    5. По рефакторингу могу порекомендовать книгу "Работа с унаследованным кодом" Майкл Физерс. Кроме того об этом много статей в уже упомянутом блоге Александра Бындю. Грубо говоря я бы назвал ту подборку статей "как не переписывать и начать жить"
    6. Ещё один блог где собрано большое количество полезных материалов по ООП, рефакторингу, проектированию, это блог Сергея Теплякова Вот ссылка на его подборку книг по теме: sergeyteplyakov.blogspot.com/2013/08/blog-post.html
    7. Изучайте материалы постепенно. Не стоит сразу пытаться воткнуть только что полученные знания в первый попавшийся проект. Обсуждайте возможные решения с коллегами. Со временем они также станут поддерживать эту практику. Если есть возможность, попрактикуйте парное программирование. Причём не обязательно с более опытным коллегой. Иногда вопросы задаваемые наивным человеком заставляют задуматься гораздо крепче, чем ответы получаемые от мудрецов.
    Ответ написан
    1 комментарий
  • Python, postgress, pandas - куда утекает память?

    @Arlekcangp
    Разработчик, Лид, Архитектор ПО
    Я не специалист по пайтону, но присматриваюсь и ваш код меня заинтересовал. Немного погуглив я нашел такой похожий вопрос на SO ( https://stackoverflow.com/questions/39100971/how-d... ), но c более простым кодом:
    import pandas
    df = pandas.read_csv('large_txt_file.txt')
    del df

    Уже этого достаточно, что бы память не возвращалась в ОС. Автор вопроса подозревал Pandas, но как пояснили в ответах, это особенность самого пайтона:
    Reducing memory usage in Python is difficult, because Python does not actually release memory back to the operating system. If you delete objects, then the memory is available to new Python objects, but not free()'d back to the system (see this question).

    Т е если вы смотрите количество используемой процессом памяти, то оно будет только увеличиваться. Первое, что я бы попробовал, это поменять ваш код так:
    for station in config.STATIONS_LIST:
        sql_query = f"select * from table where  where station = '{station}'"
        df = pd.read_sql(sql_query, con=connection_pg)
        filename = f'data_{station}'
        filename_with_path = os.path.join(config.OUTPUT_PATH, filename)
        compression_options = dict(method='zip', archive_name=f'{filename}.csv')
        df.to_csv(f'{filename_with_path}.zip', compression=compression_options, index=False)
        <b>df = ' '</b>
        gc.collect()

    Т е не удалять переменную, а переприсвоить. Некоторые говорят, что это помогает (если честно, мне в такое с трудом верится, но я не знаю пайтона) Среди других рекомендаций: загружать данные меньшими порциями и офлоудить работу другому процессу, который затем убивается и память освобождается ОС. (на мой взгляд способ хороший, хоть и не слишком архитектурно-правильный, но гарантировано добавит стабильности и застрахует даже от будущих утечек, если они появятся либо в вашем коде либо в новых версиях библиотек)
    Другой вопрос, почему это увеличение не останавливается. Если это всё дело происходит на linux то я бы попробовал ограничить пайтону память (первое что нагуглилось: https://www.geeksforgeeks.org/python-how-to-put-li...) и посмотрел будет ли при этом интерпретатор умирать по причине недостатка памяти. Если будет, то на SO рекомендовали такое средство: https://mg.pov.lt/objgraph/ Этим можно посмотреть что именно потребляет память.
    Ответ написан
    5 комментариев
  • Стоит ли использовать микрокомпьютер для удаленной работы?

    @Arlekcangp
    Разработчик, Лид, Архитектор ПО
    Ох, тут уже на предлагали... Что ж, я не путешествую, но вопрос "куда можно запихнуть мини ПК" меня какое то время мучил, и я пришёл к выводу, что из мини ПК может получится неплохая ретро-консоль. И естественно она должна быть портативной. И раз вам так хочется сменить ноут на миник, то поделюсь к каким выводам я пришёл.
    1. Покупать что то излишне миниатюризированное формата raspberry Pi не стоит. Это вариант для умного дома и управления прочими прибамбасами, а не для работы или тем более игр. Естественно туда же идут и все варианты, напоминающие флэшку. При попытке работать будете плеваться от тормозов.
    2. Излишне тесный корпус приводит к нагреву и требует излишнего охлаждения. А это шум, доп потребление, механические детали и повышение стоимости . В тесных корпусах возможно только CPU с очень низким теплопакетом, а значит смотри пункт первый, тормоза неизбежны.
    3. Только не Андроид (и arm)! Не знаю кому как, а мне софта на адройде мало И практически отсутствует open source варианты. Так что и всякие планшеты и тв-боксы и подобный хлам - в лес. К тому же можно ещё испытать и прекрасный геморрой при попытке с планшета вывести изображение на монитор. Не все планшеты такое поддерживают.
    4. И не intel NUC или тем более его китайские собратья. В качестве самого NUC сомневаться не приходится. Однако, тем не менее, это проприетарная платформа и от ноутбука отличается только отсутствием монитора... Ничего толком не известно сколько будет стоить апгрейд и будет ли он вообще возможен. А спрашивается зачем тогда оно ? По этой же причине и не мак-мини и прочие надкусанности. Сюда же ещё и дефицит софта в сравнении с x86 как и у андройда.
    Итого: приходим к выводу, что это будет самосбор:
    Вариант 1: Корпус формата mini ITX - не слишком маленьких, что бы туда влезало типовое железо и небыло проблем с охладом и не слишком большой потому что портативность. Объём 2-5 литров. Плюсы:
    - можно собрать x86 на десктопном процессоре, с приличным объёмом оперативки и даже встроенным SSD приличного объёма (хотя уж с этим то сейчас проблем нет и полно внешних корпусов для SSD, но встроенный вы не будете искать среди кучи хлама после очередного переезда.
    - всё ещё поддается апгрейду, но занимает места примерно столько же сколько и ноут. Допускает относительно хорошее охлаждение Широкий выбор самих корпусов, как у именитых брендов так и китайцев. Полностью сменная начинка, которая никуда не исчезнет с рынка. И даже сменный процессор и память. И в тоже время - внешний БП как и в ноуте (правда без батареи, но как говорится, кто мешает и её воткнуть...), может оказаться полезной вещью в дороге, если нужно запитать ещё какие то низковольтные устройства от 12 вольт
    - Возможность втыкивать достаточно мощный процессор. Я присматривался к райзену с интегрированным GPU. На таком не только работать, но и во что то не сильно требовательное можно поиграть.
    - Можно настроить dual boot - windows + linux для тех кому нужно. Полноценная сеть (уже не во всех ноутбуках она есть. А во всяких малиноподобных вариантах - как правило, сеть медленная)
    - Широкий набор внешних портов. Любой ноут здесь проигрывает. (другой вопрос нужно ли это для вашей работы)
    - Прочный металлический корпус (полно как стальных, так и алюминивых вариантов. Есть и пластиковые, но я бы брал металл как из-за прочности так и из-за охлаждения)
    Минусы:
    - Цена Как говорится, из разряда "отложу как я это до лучших времён" С такими ценами, как сегодня, вообще не выгодно менять железо.
    - Надо собирать самому (ну для меня это не минус, а для вас - не знаю)
    - Размер и вес скорее всего будет немного больше того же Intel NUC но, считаю, что это не существенно.
    - мощность системы не должно превышать 150-200 ватт иначе будет перегреваться да и БП на такую мощность найти сложновато. Вполне можно уложиться если брать CPU с TDP 45-65 Ватт

    Вариант 2 Мощный самосбор в корпусе miniITX объёмом до 12 литров. Плюсы:
    - Всё тоже самое что и варианте 1 но немного дешевле т к корпус больше
    - Уже есть возможность запихнуть какой-никакой дискретный GPU Но тут надо заранее выбирать и велика вероятность того, что этот элемент потом обновлять будет тяжело, т к трёх-слотовый монстр длиной 30+ сантиметров туда не встанет, а среднебюджетных вариантов как то не видно на горизонте Только слабенькие GPU делают в усеченных размерах и их ещё найти нужно.
    - можно поставить очень хорошее охлаждение при желании. Но тоже нужно об этом думать на этапе выбора
    корпуса
    - Уже относительно стандартные внутренние БП на 300-500 ватт
    Минусы:
    - Вес. Если для вас не критично вес в несколько килограмм (сейчас только видяхи по килограмму весят =)
    - Размер 12 литров - это как два с половиной пятилитровых бутыля. Для сравнения, обычно корпуса ноутбуков не превышают 2-х литров. Очевидно, при авиаперелетах это скорее всего в багаж отправится...
    Ответ написан
    1 комментарий
  • Как обойти систему верификации при автоматизированной регистрации аккаунта?

    @Arlekcangp
    Разработчик, Лид, Архитектор ПО
    можно также автоматически заходить на указанную почту, в которую пришло письмо с кодом, парсить его оттуда и вставлять в поле при регистрации

    Это имеет смысл делать, если тестируете саму регистрацию. В других тестах есть несколько вариантов в зависимости от того где и на какой базе происходит прогон тестов (очевидно, это функциональные или интеграционные тесты, раз речь идёт о сторонней почте) Если тесты прогоняются на игрушечной базе, пустой базе или каком-то стэйдж, который достаточно закрыт от внешнего мира, то вы можете деплоить туда доп. модуль, который будет содержать апи регистрации без проверки кода из почты. На продакшен этот модуль деплоиться не будет и он должен быть сделан так, что бы безболезненно отрезаться от приложения не нарушая его функционал (и естественно с учётом требований безопасности) Разумеется, это только для тестов, которые саму по себе регистрацию не проверяют, но для них необходим свежий зарегистрированный пользователь.
    Если же тесты гоняются на проде, то очевидно этот способ не подходит и остаётся либо заранее зарегистрированный пользователь всегда один и тот же. Либо каждый раз регистрировать ящик и потом его проверять.
    Ответ написан
    Комментировать