Русский
Русский
English
Статистика
Реклама

Рефлексия

Поговори сам с собой ученые научили роботов самоанализу и улучшили результаты их работы

30.04.2021 20:15:00 | Автор: admin

Многие из нас говорят с сами с собой, только не вслух, конечно, а мысленно. Психологи говорят, что это вполне нормально и даже полезно. Причем не только для человека, но и для роботов. Так, итальянские ученые сознательно научили человекоподобных роботов думать вслух. Благодаря экспериментам они доказали, что внутренний монолог помогает разобраться в сложных и неопределенных ситуациях даже автоматическим системам. Центральной фигурой эксперимента стал японский робот-помощник Pepper, разработанный корпорацией SoftBank Robotics.

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

Суть эксперимента


Эксперимент провели двое ученых из Университета Палермо. Они интегрировали в операционную систему робота модель внутренней речи на основе ACT-R. Это система управления роботом, включающая стандартные инструменты преобразования текста в речь и наоборот.

Цели эксперимента:

  1. Научить робота сопровождать действия голосом: комментировать все итерации в моменте.
  2. Провести оценку результатов и понять, как влияет проговаривание на итоги.

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

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

Что произошло?


Роботу показали схему, отвечающую за нормы этикета.

Выглядела она так:

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

С Pepper взаимодействовали по трем сценариям:

  1. Простое и понятное выполнение указаний без противоречий. Здесь все логично.
  2. Наличие противоречия между действиями и требованиями. В этом случае робот видел проблему: выполнить действие, несмотря на противоречие, или нет (нарушить правила или нет).
  3. Ложное требование: положить предмет, который уже есть. Робот сталкивался с дилеммой.


Все проведенные попытки оценили по нескольким показателям:

  • время принятия решения;
  • время выполнения задания;
  • количество удачных попыток;
  • прозрачность операций.

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

Итоги


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

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

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

Вот результаты эксперимента, здесь два блока по 30 итераций.

Первый блок с включенным внутренним монологом:

26 число успешных попыток;
28 число прозрачных итераций.

Второй блок с выключенным внутренним монологом:

18 число успешных попыток;
12 число прозрачных взаимодействий.

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

Подробнее..

Перевод Вы разработчик?

20.10.2020 14:07:37 | Автор: admin
Перевод статьи Are you developer?


На самом деле, ты не разработчик. Рано или поздно люди поймут, что ты не понимаешь, о чём говоришь. Ты недостаточно подкован.

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

Открою секрет по поводу этого чувства. Готовы?

Все разработчики в той или иной мере так думают и чувствуют. Это встречается так часто, что превратилось в протоптанную тропу, по которой мы все идём.

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

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

Сегодня мы поговорим о чувствах. Зачем? Затем что разработчики люди, а не машины. Наши чувства влияют на качество нашей работы и удовольствие от неё.

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

Определение разработчика


Начнём с главного определения. Готовы?

Разработчик это человек, который пишет код.

И всё! Не существует Священного Совета, наделяющего вас званием разработчика, когда вы достигаете некоего уровня. Никто не отправляет вам сертификат на пергаменте с витиеватыми подписями и печатью из серебристой фольги. Не существует официального экзамена, который нужно пройти, чтобы наконец-то, наконец-то вы получили право называться разработчиком.

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

Если вы пишете код, то вы разработчик.

Не имеет значения, пишете ли вы профессионально или в свободное от работы время. Это можно делать в CLI, IDE, вебе или внутри другого приложения. Это может быть клиентский код, серверный, встроенный, облачный, локальный, удалённый или какой-нибудь другой. Если это код, то вы участник клуба.

Синдром самозванца


Синдромом самозванца называется состояние, когда вы чувствуете себя мошенником: мол, вы недостаточно хороши и кто-нибудь это в конце концов поймёт, либо вам вообще тут не место. Большинство разработчиков ощущают себя так в какие-то моменты. Не верите? Дэвид Уолш написал прекрасную статью о своём подобном опыте.

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


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

Хоккейная клюшка обучения


Когда ты впервые начинаешь писать код, всё запредельно сложно. По мере роста навыков становится легче. И в конце концов ты перестаёшь думать о коде и начинаешь думать о решаемой задаче. Это как с вождением: после определённой практики ты уже неосознанно жмёшь на педали. Кто-то называет это состояние потоком.

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


Когда проходишь половину пути, попадаешь в ловушку.

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

Совершенно нормально ощущать, что тебе нужно многое узнать. У всех такое ощущение. У большинства разработчиков это чувство не проходит совсем. Вот одно из моих любимых высказываний Джона Арчибальда Уилера:

По мере того, как растёт наш остров знаний, растут и берега нашего невежества.

Чем больше мы узнаём, тем больше понимаем, сколько ещё нужно узнать.

Настрой на рост


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

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

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

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

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

Хитрость в том, чтобы переформатировать своё отношение к какой-то теме. Разработка ПО это навык, а не врождённая способность. Чтобы ему научиться, требуется терпение и практика.

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

В чём смысл?


Что всё это означает? Что вы разработчик. Добро пожаловать в сообщество разработчиков.

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

И самое главное: идите и пишите код.
Подробнее..

Тривиальная и неправильная облачная компиляция

28.01.2021 00:21:03 | Автор: admin


Введение


Данная статья не история успеха, а скорее руководство как не надо делать. Весной 2020 для поддержания спортивного тонуса участвовал в студенческом хакатоне (спойлер: заняли 2-е место). Удивительно, но задача из полуфинала оказалась более интересной и сложной чем финальная. Как вы поняли, о ней и своём решении расскажу под катом.


Задача


Данный кейс был предложен Deutsche Bank в направлении WEB-разработка.
Необходимо было разработать онлайн-редактор для проекта Алгосимулятор тестового стенда для проверки работы алгоритмов электронной торговли на языке Java. Каждый алгоритм реализуется в виде наследника класса AbstractTradingAlgorythm.


AbstractTradingAlgorythm.java
public abstract class AbstractTradingAlgorithm {    abstract void handleTicker(Ticker ticker) throws Exception;    public void receiveTick(String tick) throws Exception {        handleTicker(Ticker.parse(tick));    }    static class Ticker {        String pair;        double price;       static Ticker parse(String tick) {           Ticker ticker = new Ticker();           String[] tickerSplit = tick.split(",");           ticker.pair = tickerSplit[0];           ticker.price = Double.valueOf(tickerSplit[1]);           return ticker;       }    }}

Сам же редактор во время работы говорит тебе три вещи:


  1. Наследуешь ли ты правильный класс
  2. Будут ли ошибки на этапе компиляции
  3. Успешен ли тестовый прогон алгоритма. В данном случае подразумевается, что "В результате вызова new <ClassName>().receiveTick(RUBHGD,100.1) отсутствуют runtime exceptions".


Ну окей, скелет веб-сервиса через spring накидать дело на 5-10 минут. Пункт 1 работа для регулярных выражений, поэтому даже думать об этом сейчас не буду. Для пункта 2 можно конечно написать синтаксический анализатор, но зачем, когда это уже сделали за меня. Может и пункт 3 получится сделать, использовав наработки по пункту 2. В общем, дело за малым, уместить в один метод, ну например, компиляцию исходного кода программы на Java, переданного в контроллер строкой.


Решение


Здесь и начинается самое интересное. Забегая вперёд, как сделали другие ребята: установили на машину джаву, отдавали команды на ось и грепали stdout. Конечно, это более универсальный метод, но во-первых, нам сказали слово Java, а во-вторых...



у каждого свой путь.


Естественно, Java окружение устанавливать и настраивать всё же придётся. Правда компилировать и исполнять код мы будем не в терминале, а, как бы это ни звучало, в коде. Начиная с 6 версии, в Java SE присутствует пакет javax.tools, добавленный в стандартный API для компиляции исходного кода Java.
Теперь привычные понятия такие, как файлы с исходным кодом, параметры компилятора, каталоги с выходными файлами, сообщения компилятора, превратились в абстракции, используемые при работе с интерфейсом JavaCompiler, через реализации которого ведётся основная работа с задачами компиляции. Подробней о нём можно прочитать в официальной документации. Главное, что оттуда сейчас перейдёт моментально в текст статьи, это класс JavaSourceFromString. Дело в том, что, по умолчанию, исходный код загружается из файловой системы. В нашем же случае исходный код будет приходить строкой извне.


JavaSourceFromString.java
import javax.tools.SimpleJavaFileObject;import java.net.URI;public class JavaSourceFromString extends SimpleJavaFileObject {    final String code;    public JavaSourceFromString(String name, String code) {        super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);        this.code = code;    }    @Override    public CharSequence getCharContent(boolean ignoreEncodingErrors) {        return code;    }}

Далее, в принципе уже ничего сложного нет. Получаем строку, имя класса и преобразуем их в объект JavaFileObject. Этот объект передаём в компилятор, компилируем и собираем вывод, который и возвращаем на клиент.
Сделаем класс Validator, в котором инкапсулируем процесс компиляции и тестового прогона некоторого исходника.


public class Validator {    private JavaSourceFromString sourceObject;    public Validator(String className, String source) {        sourceObject = new JavaSourceFromString(className, source);    }}

Далее добавим компиляцию.


public class Validator {    ...    public List<Diagnostic<? extends JavaFileObject>> compile() {        // получаем компилятор, установленный в системе        var compiler = ToolProvider.getSystemJavaCompiler();        // компилируем        var compilationUnits = Collections.singletonList(sourceObject);        var diagnostics = new DiagnosticCollector<JavaFileObject>();        compiler.getTask(null, null, diagnostics, null, null, compilationUnits).call();        // возворащаем диагностику        return diagnostics.getDiagnostics();    }}

Пользоваться этим можно как-то так.


public void MyMethod() {        var className = "TradeAlgo";        var sourceString = "public class TradeAlgo extends AbstractTradingAlgorithm{\n" +                "@Override\n" +                "    void handleTicker(Ticker ticker) throws Exception {\n" +                "       System.out.println(\"TradeAlgo::handleTicker\");\n" +                "    }\n" +                "}\n";        var validator = new Validator(className, sourceString);        for (var message : validator.compile()) {            System.out.println(message);        }    }

При этом, если компиляция прошла успешно, то возвращённый методом compile список будет пуст. Что интересно? А вот что.

На приведённом изображении вы можете видеть директорию проекта после завершения выполнения программы, во время выполнения которой была осуществлена компиляция. Красным прямоугольником обведены .class файлы, сгенерированные компилятором. Куда их девать, и как это чистить, не знаю жду в комментариях. Но что это значит? Что скомпилированные классы присоединяются в runtime, и там их можно использовать. А значит, следующий пункт задачи решается тривиально с помощью средств рефлексии.
Создадим вспомогательный POJO для хранения результата прогона.


TestResult.java
public class TestResult {    private boolean success;    private String comment;    public TestResult(boolean success, String comment) {        this.success = success;        this.comment = comment;    }    public boolean success() {        return success;    }    public String getComment() {        return comment;    }}

Теперь модифицируем класс Validator с учётом новых обстоятельств.


public class Validator {    ...    private String className;    private boolean compiled = false;    public Validator(String className, String source) {        this.className = className;        ...    }    ...    public TestResult testRun(String arg) {        var result = new TestResult(false, "Failed to compile");        if (compiled) {            try {                // загружаем класс                var classLoader = URLClassLoader.newInstance(new URL[]{new File("").toURI().toURL()});                var c = Class.forName(className, true, classLoader);                // создаём объект класса                var constructor = c.getConstructor();                var instance = constructor.newInstance();                // выполняем целевой метод                c.getDeclaredMethod("receiveTick", String.class).invoke(instance, arg);                result = new TestResult(true, "Success");            } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | ClassNotFoundException | RuntimeException | MalformedURLException | InstantiationException e) {                var sw = new StringWriter();                e.printStackTrace(new PrintWriter(sw));                result = new TestResult(false, sw.toString());            }        }        return result;    }}

Возвращаясь к предыдущему примеру использования, можно дописать туда такие строчки.


public void MyMethod() {        ...        var result = validator.testRun("RUBHGD,100.1");        System.out.println(result.success() + " " + result.getComment());    }

Вставить этот код в реализацию API контроллера задача нетрудная, поэтому подробности её решения можно опустить.


Какие проблемы?


  1. Ещё раз напомню про кучу .class файлов.


  2. Поскольку опять же идёт работа с компиляцией некоторых классов, есть риск отказа в записи любого непредвиденного .class файла.


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



Поэтому делать в точности как я не надо)


P.S. Ссылка на гитхаб с исходным кодом из статьи.

Подробнее..

Оживляем деревья выражений кодогенерацией

02.01.2021 00:07:07 | Автор: admin

Деревья выражений System.Linq.Expressions дают возможность выразить намерения не только самим кодом, но и его структурой, синтаксисом.

Их создание из лямбда-выражений это, по сути, синтаксический сахар, при котором пишется обычный код, а компилятор строит из него синтаксическое дерево (AST), которое в том числе включает ссылки на объекты в памяти, захватывает переменные. Это позволяет манипулировать не только данными, но и кодом, в контексте которого они используются: переписывать, дополнять, пересылать, а уже потом компилировать и выполнять.

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

(бенчмарк)

Действие

Время, нс

Cached Compile Invoke

0.5895 0.0132 ns

Compile and Invoke

83,292.3139 922.4315 ns

Это особенно обидно, когда выражение простое, например содержит только доступ к свойству (в библиотеках для маппинга, сериализации, дата-байндинга), вызову конструктора или метода (для IoC/DI решений).

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

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

  • Встроенную интерпретацию.
    Необходимость использования интерпретатора вместо компилятора указывается соответствующим флагом:

    Expression.Compile(preferInterpretation: true)
    

    Происходит через рефлексию, но с накладными расходами на формирование стека инструкций.

    Для платформ Xamarin.iOS, Xamarin.watchOS, Xamarin.tvOS, Mono.PS4 и Mono.XBox стандартная компиляция через генерацию IL (System.Reflection.Emit) долгое время была недоступна и на данный момент под капотом всегда откатывается к этому варианту.

  • FastExpressionCompile от @dadhi.
    Ускоряет компиляцию за счет оптимизиpованной генерации IL и с меньшим количеством проверок совместимости.

    На платформах без поддержки JIT компиляции может использоваться только с включенным Mono Interpreter.

  • Ручную интерпретацию.
    Используется для оптимизации вызовов рефлексии под специальные сценарии использования, например для добавления кэширования отдельных вызовов.

    Интерпретируя вручную, уже можно воспользоваться способами ускорения рефлексии. Самые эффективные из них, например Fasterflect, используют System.Reflection.Emit и на некоторых платформах так же могут требовать включения Mono Interpreter.

Для случаев, когда производительности указанных методов недостаточно, напрашивается решение:

Компилировать выражения или какие-то их части во время написания кода (design-time) или сборки (compile-time).

Для compile-time компиляции делегатов к фрагментам деревьев выражений требуется сгенерировать соответствующий код.

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

От самого API требуется только давать нужный делегат по ключу, как в словаре. У интересующих нас фрагментов кода: методов, конструкторов и свойств на стыке run-time и compile-time естественный идентификатор это сигнатура. По ней генерируемый код будет класть делегаты в словарь, а клиенты забирать.

Например, для класса со свойством

namespace Namespace{  public class TestClass  {    public int Property { get; set; }  }}

используемым внутри System.Linq.Expressions.Expression<T> лямбды

Expression<Func<TestClass, int>> expression = o => o.Property;

делегатами чтения и записи в общем виде являются

Func<object, object> _ = obj => ((Namespace.TestClass)obj).Property;Action<object, object> _ => (t, m) => ((Namespace.TestClass)t).Property  = (System.Int32)m;

и генерируемый код для их регистрации будет примерно таким:

namespace ExpressionDelegates.AccessorRegistration{  public static class ModuleInitializer  {    public static void Initialize()    {      ExpressionDelegates.Accessors.Add("Namespace.TestClass.Property",        getter: obj => ((Namespace.TestClass)obj).Property,        setter: (t, m) => ((Namespace.TestClass)t).Property = (System.Int32)m);    }  }}

Генерация

Наиболее известные решения для кодогенерации, на мой взгляд, это:

Отдельная область применения есть у каждого решения, и только Roslyn Source Generators умеет анализировать исходный C# код даже в процессе его набора.

Кроме того, именно Roslyn Source Generators видятся более или менее стандартом для кодогенерации, т. к. были представлены как фича основного компилятора языка и используют Roslyn API, используемый в анализаторах и code-fix.

Принцип работы Roslyn Source Generators описан в дизайн-документе (местами не актуален!) и гайде.

Вкратце: для создания генератора требуется создать реализацию интерфейса

namespace Microsoft.CodeAnalysis{  public interface ISourceGenerator  {    void Initialize(GeneratorInitializationContext context);    void Execute(GeneratorExecutionContext context);  }}

и подключить ее к проекту как анализатор.

Метод Initialize пригодится для выполнения какой-либо единоразовой логики. GeneratorInitializationContext на данный момент может быть полезен только для подключения посетителя узлов синтаксиса кода.

В Execute имеется контекст, из которого можно как собрать информацию по существующему коду, так и, собственно, добавить новый.

Для каждого файла исходного кода Roslyn предоставляет синтаксическое дерево в виде объекта SyntaxTree:

GeneratorExecutionContext.Compilation.SyntaxTrees

а так же семантическую модель:

semanticModel =  GeneratorExecutionContext.Compilation.GetSemanticModel(SyntaxTree)

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

Среди всех узлов синтаксических деревьев сборки нам нужно найти только интересующие нас лямбда-выражения типа System.Linq.Expressions.Expression<T> и отобрать из их узлов-потомков выражения, описывающие доступ к членам классов, создание объектов и вызов методов:

По семантике узла, так называемому символу (Symbol), можно определять:

  • типы, используемые выражением;

  • область видимости;

  • IsStatic, IsConst, IsReadOnly и другие характеристики.

На основе такой информации и будем генерировать подходящий код.

В Roslyn API (Microsoft.CodeAnalysis) построить сигнатуру намного проще, чем c API рефлексии (System.Reflection). Достаточно сконвертировать символ в строку при помощи методаISymbol.ToDisplayString(SymbolDisplayFormat) c подходящим форматом:

Зная сигнатуры свойства/поля, его типа и обладателя формируем строки для добавления делегатов:

Оформляем код добавления делегатов в класс и отдаем компилятору:

var sourceBuilder = new StringBuilder(@"namespace ExpressionDelegates.AccessorRegistration{  public static class ModuleInitializer  {    public static void Initialize()    {");      foreach (var line in registrationLines)      {        sourceBuilder.AppendLine();        sourceBuilder.Append(' ', 6).Append(line);      }      sourceBuilder.Append(@"    }  }}");GeneratorExecutionContext.AddSource(  "AccessorRegistration",  SourceText.From(sourceBuilder.ToString(), Encoding.UTF8));

Этот код обязательно будет добавлен в сборку ...если генератор сможет отработать :)

Дело в том, что хоть Source Generators технически и не фича языка, поддерживаются они только в проектах с C# 9+. Позволить такую роскошь без костылей и ограничений на данный момент могут только проекты на .NET 5.

Совместимость

Поддержку Roslyn Source Generators API для .NET Standard, платформ .NET Core, .NET Framework и даже Xamarin поможет организовать Uno.SourceGeneration.

Uno.SourceGeneration предоставляет собственные копии интерфейса ISourceGenerator и атрибута [Generator], которые при миграции на С# 9 меняются на оригинальные из пространства имен Microsoft.CodeAnalysis простым удалением импортов Uno:

using Uno.SourceGeneration;using GeneratorAttribute = Uno.SourceGeneration.GeneratorAttribute;using ISourceGenerator = Uno.SourceGeneration.ISourceGenerator;
Для подключения достаточно добавить несколько строк в файл проекта.

В проект, где генератор будет использоваться:

<ItemGroup>  <SourceGenerator Include="PATH\TO\GENERATOR.dll" /></ItemGroup>

Например, распространяя генератор через nuget, подключение можно осуществлять вложением MSBuild props файла со следующим путём:

Инициализация

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

Для этих целей отлично подходит Module Initializer. Это конструктор сборки (а точнее ее модуля), который запускается сразу после ее загрузки и до вызовов к остальному коду. Он давно есть в CLR, но к сожалению, в C# его поддержка c атрибутом [ModuleInitializer] добавлена только в 9 версии.

Решение по добавлению конструктора в сборку с более широкой поддержкой платформ есть у Fody плагин Fody.ModuleInit. После компиляции добавляет классы с именами ModuleInitializer в конструктор сборки. В такой класс и будем оборачивать инициализацию сгенерированных делегатов.

Подключение Fody.ModuleInit через MSBuild свойства вместо FodyWeavers.xml исключит конфликты с другими Weaver-ами Fody в проекте клиента.

Использование

Таким образом, при сборке проекта:

  1. Source Generator добавит в сборку код, регистрирующий делегаты для деревьев выражений, в обертке класса ModuleInitializer.

  2. Fody.ModuleInit добавит ModuleInitializer в конструктор сборки.

  3. Во время работы приложения при подгрузке сборки выполнится ModuleInitializer, и сгенерированные делегаты будут добавлены к использованию.

Проверяем:

Expression<Func<string, int>> expression = s => s.Length;MemberInfo accessorInfo = ((MemberExpression)expression.Body).Member;Accessor lengthAccessor = ExpressionDelegates.Accessors.Find(accessorInfo);var length = lengthAccessor.Get("17 letters string");// length == 17

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

Бенчмарки

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

Действие

Время, нс

Вызов простого делегата конструктора

4.6937 0.0443

Вызов сгенерированного делегата конструктора

5.8940 0.0459

Поиск и вызов сгенерированного делегата конструктора

191.1785 2.0766

Компиляция выражения и вызов конструктора

88,701.7674 962.4325

Вызов простого делегата доступа к свойству

1.7740 0.0291

Вызов сгенерированного делегата доступа к свойству

5.8792 0.1525

Поиск и вызов сгенерированного делегата доступа к свойству

163.2990 1.4388

Компиляция выражения и вызов геттера

88,103.7519 235.3721

Вызов простого делегата метода

1.1767 0.0289

Вызов сгенерированного делегата метода

4.1000 0.0185

Поиск и вызов сгенерированного делегата метода

186.4856 2.5224

Компиляция выражения и вызов метода

83,292.3139 922.4315

Полный вариант таблицы, с бенчмарками интерпретации.

А судя по результату профилирования поиска сгенерированного делегата, самое долгое построение сигнатуры, ключа для поиска.

Flame-график бенчмарка поиска и вызова сгенерированного делегата доступа к свойствуFlame-график бенчмарка поиска и вызова сгенерированного делегата доступа к свойству

Идеи насчёт оптимизации построения сигнатур по System.Reflection.MemberInfo приветствуются. Реализация на момент написания.

Заключение

По итогу получилось современное решение для кодогенерации с актуальной совместимостью и автоматической инициализацией.

Полный код можно посмотреть на: github/ExpressionDelegates, а подключить через nuget.

Для тех, кто будет пробовать Source Generators хотелось бы отметить несколько полезностей:

  • Source Generator Playground (github).
    Позволяет экспериментировать с Roslyn Source Generators в браузере, онлайн.

  • Окно визуализации синтаксиса для Visual Studio.
    Удобный инструмент для знакомства с Roslyn Syntax API на собственном коде.

  • Отлаживается Source Generator вызовом отладчика из его кода. Пример.
    Для этого нужен компонент Visual Studio Just-In-Time debugger и включенная настройка Tools -> Options -> Debugging -> Just-In-Time Debugging -> Managed.

  • В сгенерированных *.cs файлах срабатывают брейкпоинты, проверено в Visual Studio16.8.
    При генерации через Uno.SourceGeneration файлы размещаются по пути: \obj\{configuration}\{platform}\g\.
    С Roslyn Source Generators их появление включается через MSBuild свойство EmitCompilerGeneratedFiles.
    Стандартный путь: \obj\{configuration}\{platform}\generated\, переопределяется в свойстве CompilerGeneratedFilesOutputPath.

  • Source Generators можно конфигурировать свойствами MSBuild.
    При использовании Uno.SourceGeneration значение получают вызовом

    GeneratorExecutionContext.GetMSBuildPropertyValue(string)
    

    Для Roslyn Source Generators требуемые свойства необходимо сперва отдельно обозначить в MSBuild группе CompilerVisibleProperty и только после вызывать:

    GeneratorExecutionContext.AnalyzerConfigOptions.GlobalOptions  .TryGetValue("build_property.<PROPERTY_NAME>", out var propertyValue)
    
  • Из генератора можно кидать предупреждения и ошибки сборки.

    //Roslyn Source GeneratorsGeneratorExecutionContext.ReportDiagnostic(Diagnostic)//Uno.SourceGeneration:GeneratorExecutionContext.GetLogger().Warn/Error().
    
Подробнее..

Тревога, тревожность, стресс

12.05.2021 10:23:58 | Автор: admin

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

И, разумеется, постараюсь ответить на вопрос, что с каждым из них делать.

Стресс

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

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

Селье, автор теории стресса, различал дистресс и эустресс.

Дистресс негативен, он забирает все ресурсы, истощает нас; мы не можем справиться с ним. Эустресс положителен, он помогает нам, за счет адаптации к новым условиям, развиваться и находить новые точки роста.

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

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

В деятельности разработчика быть стрессорами и вызывать стресс могут:

конфликтные и непрозрачные отношения в команде

неясные задачи и регулярная неизвестность завтрашнего дня

чрезмерно быстрая смена задач

стихийные изменения состава команды

критика, обесценивание, ревью, направленное на подавление

Стресс возникает в ответ на новые обстоятельства чаще всего на те, которые нам некомфортны.

Пример такого нового и некомфортного обстоятельства собеседование. Для многих из нас это стресс: нас оценивают неизвестные люди, мы можем не знать и не понимать правил, по которым происходит оценка.

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

Тревога

Стресс и тревога схожи. Разница вот в чём: у стресса есть внешний раздражитель, а у тревоги его нет. В тревоге преобладает фактор неопределенности. Мы не знаем, откуда возникает угроза и есть ли она вообще. Тревога возникает как реакция на ожидание чего-то плохого, что может произойти.

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

Давайте снова возьмем в пример собеседование. Разработчик отменяет собеседование из-за тревоги, что, придя в команду, не справится с задачами. Ситуация не случилась, она находится в точке неизвестности, возникает на уровне фантазий. Но тревога присутствует.

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

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

Если резюмировать, то тревога ответная реакция на стресс, при которой не осознаются факторы, вызывающие беспокойство.

Тревожность

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

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

Что со всем этим делать

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

Классическая триада, которая рекомендована при высокой тревоге, такова:

  1. хотя бы по минимуму заниматься спортом

  2. вести дневник эмоций

  3. медитировать

А вот ещё несколько подходящих в такой ситуации практик.

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

Ищите и пробуйте способы самоподдержки от слов одобрения до прогулки и приятных действий в свой адрес.

Установите отношения со своим телом. Совсем скоро Марина Могилевцева расскажет о том, как можно обращаться с тревогой и помогать себе с опорой на тело. Следите тут или в канале psyvit; мы сообщим в канале, когда выложим статью.

Дыхание. В момент тревоги обратите внимание на свое дыхание, сделайте глубокий и спокойный вдох и сосчитайте до 4-х. Выдох также сделайте на 4 счета.

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

Увольтесь. Если ситуация на работе не меняется и вы регулярно ощущаете себя в стрессе, а в ответ слышите: Терпи!, это сигнал позаботиться о себе самостоятельно.

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

Самое главное и важное на все времена - Берегите себя!

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru