Страницы

Поиск по вопросам

понедельник, 25 ноября 2019 г.

Возврат функции из функции - как работает замыкание? [дубликат]



    На данный вопрос уже ответили:
    
        
            Как работают замыкания в JavaScript
                
                    7 ответов
                
        
    
    
Учу js и, к сожалению, не понял одну вещь

function makeCounter() {
  var currentCount = 1;

  return function() { // (**)
    return currentCount++;
  };
}

var counter = makeCounter(); // (*)

// каждый вызов увеличивает счётчик и возвращает результат
alert( counter() ); // 1
alert( counter() ); // 2
alert( counter() ); // 3

// создать другой счётчик, он будет независим от первого
var counter2 = makeCounter();
alert( counter2() ); // 1


Вопрос 1: почему при первом вызове функции переменная currentCount = 1, ведь возвращаемое функцией currentCount++, следовательно должно быть 2. Так почему 

return function()...


пропускается?

Вопрос 2: почему при вызовах функции currentCount не присваивается 1, ведь

var currentCount = 1;

    


Ответы

Ответ 1



Когда в строке var counter = makeCounter(); выполняется функция makeCounter, он присваивает значение 1 своей локальной переменной currentCount и возвращает, но не выполняе анонимную функцию. Так как анонимная функция использует переменную из внешней области видимости, создается "замыкание" (closure), которое соединяет экземпляр переменной currentCount с возвращаемым экземпляром анонимной функции.

Ответ 2



Попробую объяснить на пальцах. Данная строка кода var counter = makeCounter(); // (*) Делает примерно следующее var currentCount = 1; var counter = function() { // (**) return currentCount++; }; Только переменная currentCount не глобальная, а находится в "замыкании", то ест видна исключительно внутри функции. Собственно makeCounter() больше уже не выполняется никогда при вызове counter(), тут сама переменная counter является функцией, которая имеет исключительный доступ к переменной currentCount. Вся идея создания функции counter() не напрямую через присваивание, а через другу функцию makeCounter() заключается как раз в том, чтобы привязать к ней переменную, котору видит исключительно counter() и которая сохраняет свое значение между вызовами. В других языках программирования существуют так называемые статические переменные - их смысл как раз в том, чтобы сохранять значение между вызовами функции, но при этом быть доступными только внутри функции. Механим создания замыкания примерно такой. Когда вызывается функция для всех ее локальны переменных выделяется область памяти. Когда функция заканчивает свою работу, то эт область памяти очищается - в этом смысл локальных переменных, они живут, пока исполняетс функция. Однако если внутри функции (у вас это makeCounter()) объявить другую функци (которая тоже будет как бы локальной переменной-функцией), а потом вернуть объявленну функцию (она у вас анонимная, но назовем ее counter(), по имени переменной в которо она сохранится) вызывающему коду, то область памяти с локальными переменными будет "жить" до тех пор, пока возвращенная функция "живет" в переменной во внешнем коде. При этом к этой области памяти доступ будет иметь только, возвращенная функция. Возвращенная функция и связанная с ней область памяти называется замыканием. Если вызвать makeCounter() повторно, то выделиться новая область памяти, объявится и вернется новая функция, образуется новое замыкание - поэтому counter'ы независимы друг от друга, так как "смотрят" в разные области памяти и "видят" там разные переменные currentCount'ы Что касается постфикного (currentCount++) и префиксного (++currentCount) инкремента, то @PeterOslon хорошо объяснил.

Ответ 3



Есть разница между префиксный инкремент ++x и постфиксный инкремент x++. x++ - это возвращает значение x, потом добавляет 1 к x. ++x - добавляет 1 к x, потом возвращает значение x. Так что return function() { return currentCount++; }; вырабатывается примерно так: return function() { var temp = currentCount; currentCount = currentCount + 1; return temp; };

Бизнес-логика, что это такое?



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


Источник

Не совсем ясно, что означает этот термин 
    


Ответы

Ответ 1



Бизнес-логика - это логика доменной модели - все, что в вашем приложении происходит в терминах предметной области. Например, на SO - это все действия с пользователями, вопросами, ответами, плюсы минусы и т.д. Пример: Если пользователь не набрал ZZZ репутации - отправить его правку на проверку другими участниками - это бизнес-логика, ей место в модели. Перенаправить пользователя на страницу вопроса после его создания - не-бизнес логика, которой место в контроллере. Скрыть кнопку "Оставить комментарий" если текущий пользователь не имеет право оставлять комментарии - особенности представление данных (флага из модели) - во view. MVC позволяет выделить "не-бизнес" логику, связанную с пользовательским интерфейсом: вызовы методов модели по определенным действиям пользователя отображение/скрытие контролов подготовку данных к отправке на клиента. ... и поместить логику представления в отдельный кусок приложения - Controller. тем самым оставив в модели "чистую" бизнес-логику, не привязанную к интерфейсу пользователя. Стоит отметить, что ссылка в вопросе ведет на статью, иллюстрированную диаграммо Classic MVC. Реально в Web используется более современный вариант паттерна - MVC Model2 - и его производные. Его отличие - View не взаимодействует с моделью напрямую. Взаимодействие в современном MVC выглядит вот так:

Ответ 2



Бизнес-логика - то же самое что и логика предметной/доменной/прикладной области Допустим, вы программируете софт для приюта животных и для детского приюта. По бизнес-логике приюта для животных, предположим, котика, которого за неделю н забрали новые хозяева, надо усыпить. А до этого его надо кормить, поить и спать укладывать. По бизнес-логике детского приюта - ребенка надо кормить, поить и спать укладывать. В него нельзя втыкать шприц со смертельной дозой морфия. При этом все структуры данных, алгоритмы и т.д. - в двух программах практически одинаковы. Кроме вот этой маленькой детали. "ЭТОТ один ИФЧИК решил СУДЬБУ КОТЕЙКИ", или, например "начинающий программист УБИЛ младенца ВЕКТОРОМ" Если вы перепутаете бизнес-логику приюта для животных и детского приюта, и усыпит ребенка, а котенку подарите куклу, вы, надеюсь, попадете в тюрячку, там вам все за ООП расскажут. Не важно, бизнес это, расчет конфигурации молекул, приют или управление кораблем Бизнес-логика - это та самая часть, которая в итоге должна работать правильно и надежно, та, результатов которой ждет заказчик (котенок, ребенок) Если не отделять, допустим интерфейс от бизнес-логики, то вместо нажатия кнопки "отдат ребенка новым родителям" или "усыпить котенка", на двух аккуратных - почти похожих - пультах управления (интерфейсах) вы будете бегать туда-сюда, пытаясь понять, кого утопить, кого усыпить, кого отдать новым родителям и почему ничего не работает. Вы не отделили интерфейс (панель управления для запуска котят на луну) от бизнес-логики и все запуталось. Ну, я предупреждал. Используете вы синглтоны, очереди, базы данных, флэт-файлы, микросервисы - не важно - важно, чтобы бизнес-логика работала правильно. Под правильно подразумевается корректность результатов в приемлемое время. Все остальное ваших заказчиков не интересует. До тех пор, пока они не являются вашими владельцами. Именно поэтому вы можете продавать очень плохой - с точки зрения программиста - соф клиентам, но с трудом сможете построить на нем надежную систему. Требования бизнес-логики может быть и выполняются, но поддерживать этот код невозможно P.S. Маленький исторический экскурс. Бизнес-логикой это называется потому, что в Нормальном Мире, во Внешней Империи программирование в коммерции и корпорациях развивалось еще с 50х-60х годов: банки, страховые агентства, туроператоры, медицина. Т.е. тебе платили за то, чтобы ты внедрил требования конкретного бизнеса Хорошо, что это бизнес-логика, а не партийная логика, как в Северной Корее.

Ответ 3



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

Ответ 4



Читайте бизнес-логика, как просто логика. Всё. А отделение её от UI - то и означает: что в представлении не должно всяких обращени к БД, выборок из неё, вспомогательных функций на N-строк, например хитроумная сортировка, фильтрация, структурирование данных; шифрование данных; проверка на правильность логина/пароля и прочих каких-либо сверх манипуляций с данными. В представлении должно быть отображение конечного результата, который придет в отве на запрос к управляющему классу, а всё манипулирование (как описано выше) данными (логика) - должно происходить в другом месте. The end.

Ответ 5



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

Практическое применение Enum


Меня всегда волновал вопрос класса Enum: какие у них есть преимущества перед обычными static final примитивами?

И, если можно, поделитесь вашим опытом, в котором использование Enum было бы действительно оправданным.
    


Ответы

Ответ 1



enum имеет ряд преимуществ при использовании в сравнении с static final int. И использовать его стоит в том случае если вам нужны эти преимущества. Главным отличием является то что используя enum вы можете проверить тип данных. Например: public class SomeClass { public static int RED = 1; public static int BLUE = 2; public static int YELLOW = 3; public static int GREEN = 3; // Совпадающие значения private int color; public void setColor(int color) { this.color = color; } } При этом вы можете передать в класс любое значение int. new SomeClass().setColor(999); Следовательно, основные проблемы использования static final int это: Необходимость проверки вводимых данных. Нет никакой защиты от создания static final int переменной с дублирующимся значением. Еще одним преимуществом enum является возможность перегружать методы. public enum UnitConverter{ METERS{ @Override public double toMiles(final double meters){ return meters * 0.00062137D; } @Override public double toMeters(final double meters){ return meters; } }, MILES{ @Override public double toMiles(final double miles){ return miles; } @Override public double toMeters(final double miles){ return miles / 0.00062137D; } }; public abstract double toMiles(double unit); public abstract double toMeters(double unit); } Недостатки использования enum К ним не применимы операторы >, <, >=, <=. Следовательно enum не стоит применять в списке последовательных данных. enum также требует больше памяти для хранения чем обычная константа. IntDef и StringDef в Android Google не рекомендует использовать enum в Android приложениях из-за того что эт требует больше памяти. Вместо этого для Android можно использовать IntDef и StringDe которые позволяют ограничивать пространство значений для обычных констант. Следовательно, вы получаете главное преимущество enum не теряя в использовании памяти. Это практика повсеместно используется в стандартных классах Android: /** @hide */ @IntDef({VISIBLE, INVISIBLE, GONE}) @Retention(RetentionPolicy.SOURCE) public @interface Visibility {} /** * This view is visible. * Use with {@link #setVisibility} and {@code * android:visibility}. */ public static final int VISIBLE = 0x00000000; /** * This view is invisible, but it still takes up space for layout purposes. * Use with {@link #setVisibility} and {@code * android:visibility}. */ public static final int INVISIBLE = 0x00000004; /** * This view is invisible, and it doesn't take any space for layout * purposes. Use with {@link #setVisibility} and {@code * android:visibility}. */ public static final int GONE = 0x00000008; В принимающем методе используется специальная аннотация: public void setVisibility(@Visibility int visibility) { setFlags(visibility, VISIBILITY_MASK); if (mBackground != null) mBackground.setVisible(visibility == VISIBLE, false); } В таком случае вы не сможете использовать другую константу для этого метода: view.setVisibility(456);//Error: Must be one of: View.VISIBLE, View.INVISIBLE, View.GONE

Ответ 2



Перечисления (Enum) удобно использовать для представления множества взаимоисключающих состояний. Пример Перечисления кодов сообщения: enum MessageCode { // Установка однозначного соответствия // между элементом перечисления и целочисленным значением. UNKNOWN(0), TOAST(1), MESSAGE(2); private final int id; MessageCode(int code) { id = code; } // Метод получения целочисленного значения, // соответствующего выбранному элементу Перечисления (Enum) public int getId() { return id; } // Метод получения элемента Перечисления (Enum), // соответствующего переданному целочисленному значению public static MessageCode fromId(int code) { MessageCode[] list = MessageCode.values(); if (code >= 0 && code < list.length) return list[code]; else return MessageCode.UNKNOWN; } } Пример анализа сообщений: private Messenger chatUIThreadMessenger = new Messenger(new Handler() { @Override public void handleMessage(Message msg) { String str; switch (MessageCode.fromId(msg.what)) { // Доступ к выводу Toast'ов для Thread'ов прослушивающих Socket'ы case TOAST: str = (String) msg.obj; Toast.makeText(getBaseContext(), str, Toast.LENGTH_LONG).show(); break; // Вывод сообщений в лог чата case MESSAGE: str = (String) msg.obj; messages.add(str); messagesAdapter.notifyDataSetChanged(); final ListView listView = (ListView) findViewById(R.id.listViewChat); listView.smoothScrollToPosition(messagesAdapter.getCount() - 1); break; // Исключительная ситуация case UNKNOWN: Toast.makeText(getBaseContext(), "Messenger: неопределённый тип сообщения!", Toast.LENGTH_LONG).show(); break; default: super.handleMessage(msg); } } });

Ответ 3



Использование enum вместо констант полезно, например, когда вы хотите ограничит возможное множество значений аргумента к-л метода. Если использовать к-л константу (in или String, например), и метод, который должен её принимать будет иметь соответствующий тип аргумента, то ничто вам не помешает передать туда что-то помимо констант. Если же вы используете enum то передать что-то кроме enum у вас не получится.

Ответ 4



Enum можно использовать как реализацию паттерна Singleton. Преимущества: Сериализация из коробки Потокобезопасность из коробки Возможность использования EnumSet, EnumMap и т.д Поддержка switch Недостатки Не ленивая инициализация По мнению Joshua Bloch’а это лучший способ реализации шаблона public enum Singleton { INSTANCE; }

Как можно взаимодействовать с сайтом через консольное приложение?


Допустим, имеется сайт на котором есть какое-то поле.

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

За этими мне нужно обращаться к классу WebBrowser?

Если можно, то покажите пример(например, можно взять целевой сайт google, но бе
использования его API.)
    


Ответы

Ответ 1



Гуглим в консоле Покажу на примере программы, которая позволяет гуглить прям в консоле (извлекае первые заглавные ссылки поиска на google.com): Предварительные работы Используйте CefSharp — библиотеку-оболочку, основанную на Chromium. Очень подробно её описал в этом ответе. Устанавливается просто через Nuget пакет. Install-Package CefSharp.OffScreen -Version 57.0.0 Скопировав из приведенного мною ответа два класса (CefSharpWrapper и ConvertHelper) у вас уже готов скелет программы, с помощью которой вы можете исполнять любой JavaScript прямо из консольного приложения. Также установите x64 или x86 в качестве платформы. Платформа Any CPU поддерживается, но требует дополнительного кода. Дополнительные свойства и методы Также для данной задачи добавьте в CefSharpWrapper свойство Address: public string Address => _browser.Address; и метод WaitTillAddressChanges: public void WaitTillAddressChanges() { // wait till address changes AutoResetEvent waitHandle = new AutoResetEvent(false); EventHandler onAddressChanged = null; onAddressChanged = (sender, e) => { _browser.AddressChanged -= onAddressChanged; waitHandle.Set(); }; _browser.AddressChanged += onAddressChanged; waitHandle.WaitOne(); } Пример самой программы Вот пример самой программы (класс Program, метод Main): public class Program { private static void Main() { MainAsync().Wait(); } private static async Task MainAsync() { CefSharpWrapper wrapper = new CefSharpWrapper(); wrapper.InitializeBrowser(); Console.Write("Введите поисковой запрос: "); string searchText = Console.ReadLine(); string[] urls = await wrapper.GetResultAfterPageLoad("https://google.com", async () => { await wrapper.EvaluateJavascript( // заполняем тесковое поле $@"document.getElementById('lst-ib').value = '{searchText}'; // выполняем submit поисковой формы document.getElementById('tsf').submit()"); // Ждём когда перейдёт на результаты поиска wrapper.WaitTillAddressChanges(); // Когда страница результатов поиска полностью подгрузится, излекае результаты return await wrapper.GetResultAfterPageLoad(wrapper.Address, async () => await wrapper.EvaluateJavascript( // получаем результаты "Array.prototype.map.call(document.querySelectorAll('h3.r > a'), (a) => a.href);")); }); Console.WriteLine("Первые ссылки поиска:"); foreach (string url in urls) { Console.WriteLine(url); } wrapper.ShutdownBrowser(); } } Результаты программы К примеру, если я введу "parse html C#": AJAX Также довольно удобно работать с AJAX c помощью с этой библиотеки.

Ответ 2



Если Вам надо закодить лишь какие-то определенные, известные части какого-то сайт - можно просто вытянуть нужные запросы, которые посылаются браузером (вычленить их можно в консоли разработчика браузера, обычно на вкладке Network) и посылать этот запрос в программе, например через HttpClient. Если же изначально самих запросов Вы знать не можете (получать надо динамически в самой программе), то может 2 варианта событий: Если сайт может работать без использования ajax - парсить страничку на наличие тэг
и отталкиваться от него. В нем есть атрибут action в котором содержится пут и атрибут encoded, который указывает формат кодировки (если нет - по умолчанию "application/x-www-form-urlencoded", вроде бы, хотя точно лучше посмотреть через консоль разработчика). Все это собираете, в том числе и параметры внутри тэга , отправляете через HttpClient и получаете ответ Если сайт работает только через ajax - тут уже сложнее и другого выхода кроме как вручную смотреть через Network какие запросы формируются и жетско их кодить я не вижу Но в большинстве случаев хватает и обычной отправки заранее подготовленных (вычлененных) запросов со своими параметрами. Так же можно привязать к HttpClient'у HttpClientHandler для хранения кукисов и других плюшек. Например Вам надо добавить пост на стену в вк определенное время - алгоритм будет такой: Создать HttpClient с привязанным к нему HttpClientHandler'ом Отправить через него POST запрос на нужный адрес (посмотреть можно на странице в в исходниках или вычленяем через консоль разработчика) со своими логином и паролем в качестве значений. Этот запрос вернет ответ, но в данном случае он не нужен, просто ждем пока придет HttpResponseMessage, чтоб нам пришли куки Отправить еще один POST запрос, который добавит сообщение на стену

Ответ 3



Нашел интересную либу Selenium с помощью которой, через драйвера можно взаимодействовать с реальным браузером. Довольная простая в управлении: using OpenQA.Selenium; using OpenQA.Selenium.Firefox; // Requires reference to WebDriver.Support.dll using OpenQA.Selenium.Support.UI; class GoogleSuggest { static void Main(string[] args) { // Create a new instance of the Firefox driver. // Note that it is wrapped in a using clause so that the browser is closed // and the webdriver is disposed (even in the face of exceptions). // Also note that the remainder of the code relies on the interface, // not the implementation. // Further note that other drivers (InternetExplorerDriver, // ChromeDriver, etc.) will require further configuration // before this example will work. See the wiki pages for the // individual drivers at http://code.google.com/p/selenium/wiki // for further information. using (IWebDriver driver = new FirefoxDriver()) { //Notice navigation is slightly different than the Java version //This is because 'get' is a keyword in C# driver.Navigate().GoToUrl("http://www.google.com/"); // Find the text input element by its name IWebElement query = driver.FindElement(By.Name("q")); // Enter something to search for query.SendKeys("Cheese"); // Now submit the form. WebDriver will find the form for us from the element query.Submit(); // Google's search is rendered dynamically with JavaScript. // Wait for the page to load, timeout after 10 seconds var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10)); wait.Until(d => d.Title.StartsWith("cheese", StringComparison.OrdinalIgnoreCase)); // Should see: "Cheese - Google Search" (for an English locale) Console.WriteLine("Page title is: " + driver.Title); } } } Однако, все равно хотелось узнать, как можно сделать это стандартными средствами .NET.

Ответ 4



Уточните каким образом происходит обработка введенного текста в поле на сайте? Сайт отправляет его GET/POST или обрабатывает иначе? Приведу ход своих мыслей на примере googl'a и GET.. Зачем нам эмулировать действия пользователя на сайте если мы напрямую можем работать с отправлением запроса, получением результата и дальнейшим парсингом string query_user = Console.ReadLine(); string url = "https://www.google.ru/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#newwindow=1&q=" + query_user; HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); HttpWebResponse response = (HttpWebResponse)request.GetResponse(); И дальше извлекаем из переменной response все что необходимо

Ответ 5



Все зависит от твоих потребностей и от сайта: Используется ли на сайте AJAX Есть ли там сложно-обходимые капчи вроде Re-Capcha На сколько сложная последовательность действий нужна На сколько важно быстродействие На сколько будет загруженной страница(это может быть просто страница, а может быть бесконечная прокрутка) Нужна ли кросплатформенность решения Используются ли какие-то хитрые технологии на сайтах вроде CORS В принципе если обобщить всю информацию поданную НИЖЕ и привести к максимально универсальному ответу: а. если нужно самое быстрое "одноразовое" решение и скорость работы не важна -- значит Selenium б. Если это задача по тестированию - Selenium в. Если нужна универсальность решения и максимально простая поддержка на долгих периодах времени и не важно сколько займет кодинг: CefSharp для прохождения капчи/рекапчи вытянуть из CefSharp куки, всунуть их в дотнетовские куки а дальше взаимодействовать с сайтом через HttpWebRequest запросы Детальнее же про каждый из способов.... WebBrowser Минуса Очень бедное управление. Сложный в работе. графический элемент, а значит будет жрать оперативу и работать медленно не работает с бесконечными страницами гипер кривая работа с JS. Некоторые сайты просто не отображаются. Устаревший инструмент. Плюса +? в теории -кросплатформенный, но не уверен. Прямая работа через HttpWebRequest Post/Get запросы с последующим парсингом. Нужну последовательность можно узнать в консоли разработчика браузера, на вкладке Network. Желательно использовать НЕ низкоуровневые запросы а какие-то библиотеки для REST запросов. Будет быстрее значительно код писатся. Минуса если на сайте AJAX - работать так может быть сложно. никак не пройти Re-Capcha. Может быть затруднительным прохождение капч в принципе. Плюса если сайт часто изменяется, реже будет ламатся код (фрондтенд менее стабилен че бекенд, так что идеальное решение для взаимодействия с бекендом!) Самое высокое быстродействие. Вы можете хоть тысячи страниц в паралели проганять. Может работать с "бесконечными" страницами любого размера. [Хотя для этого нужн будет попотеть немного] прекрасная кросплатформенность кода Selenium - в общем и целом он предназначен именно для автоматизированного тестировани веб-сайтов. В том числе и с аджакс-технологией. То есть он может делать практическ все действия, которые могут делать в браузере люди: находиль элементы интерфейса сайта, посылать в них нажатия кнопок, скролить, делать скриншоты, проверять на видимость/доступность и т.д. Т.к. указан C# тег, а так же было сказано про отсутствие лишних окон, нужно использоват связку: Selenium Web-Driver и PhantomJS. Фантом - это безюайный браузер на движке как у хрома. Он может все то же самое что и простой браузер, только не показывается визуально и не тратит ресурсы на отрисовку. Отдельно хочу сказать что сам по себе селениум довольно неудобен и далек от ООП Проблема в том, что выбора-то особенно и нету... Минуса Он не ООПшный и довольно кривой в использовании очень хреново работает с большими страницами очень медленный нужно использовать сторонние браузеры и в проэкт качать доп.библиотеки для поддержки нужного браузера Не умеет работать с внешними окнами. Например с системным окном аутентификации н сервер (с версии 3.4 умеет). Или с окнами Open/Save file (для этого есть костыли а так же я написал полууниверсальное решение: https://github.com/ukushu/DialogCapabilities ) Плюса На нем вполне можно проходить ReCapcha. При помощи юзера, правда :) Можно пройти и без юзера, если ипользовать аудиокапчу + распознавание текста с ауди через googleAPI. Например, вот здесь есть реализация прохождения рекапчи ботом, но на питоне: https://github.com/eastee/rebreakcaptcha/blob/master/rebreakcaptcha.py Вполне может работать с AJAX. А если допилять костыли, то, даже, вполне неплохо работать. Работает с посделовательностями действий любой сложности. В том числе всякие драг енд дропы. Может запускатся на многих компьютерах в паралели Не уверен ? понятия не имею по поводу кросплатформенности Оболочки вокруг готового движка вроде Хромиума. За пример - CefSharp. Минуса хреново работает с большими/бесконечными страницами относительно медленный нужно качать несколько библиотек многий функционал доступный с коробки в селениуме нужно будет реализовывать самому через JS код. Плюса На нем вполне можно проходить ReCapcha. При помощи юзера, правда :) работает с AJAX Работает с посделовательностями действий любой сложности Умеет работать с внешними окнами. Например с системным окном аутентификации на сервер. Не уверен: ? понятия не имею по поводу кросплатформенности. Скорее всего есть

Ответ 6



В заголовке вопроса - консольное приложение. Но, могу предположить по тексту В это поле нужно послать какое-то значение, нажать на кнопку, затем получить новую страницу, где перейти по первой ссылке. возможно вам подойдёт эмуляция действий пользователя в браузере. Про эмуляцию в браузере: Очень точная эмуляция действий пользователя делается с помощью написания собственног расширения для браузера (Firefox/GoogleChrome например) - от настоящего пользователя не отличить. Плагин может полностью перехватывать управление - например открыть вкладку с определённым сайтом, и "захватить" её. Внутри плагина разрабатывается некая сущность, которая может эмулировать движени и клики мышки от объекта к объекту: сами объекты с чужого сайта выбираются, например, простым jQuery - так-же как и в коде любого сайта. Также сущности нужно добавить возможность эмулировать ввод текста в любое поле. Эмуляция может происходить посылкой trusted DOM событий (это код внути плагинов умеет) или можно напрямую использовать WinApi и его PostMessageW (по крайней мере аддоны фаерфокса умеют подключать любую dll и юзать её затем из JS) - железобетонный способ. В общем технически такой эмулятор от пользователя не отличить. Под подобную деятельность заточен известный аддон Greasemonkey . Но если масштаб эмуляции планируются большими (политика добавления определённых скриптов к определённым страницам недостаточна) - не рекомендовал бы, ибо его логики не хватит. Если нужно к эмуляции добавить ещё и управление браузером из консоли, используйт selenium. Это не прокатит, если вы хотите чтобы это всё работало со стандартного сервер - то есть без GUI. Но по своей практике скажу - использовали эмуляторы часто, для них у нас были отдельные сервера с виндой, с GUI. Без GUI, с голой консолью, есть мнение что качественно эмулировать не выйдет.. Классическое использование таких эмуляторов - боты-парсеры, боты-спамеры, боты-игровые, авто-тестирование собственного сайта.

Ответ 7



На мой взгляд, лучшее решение - это WatiN. WatiN Features Automate all major HTML elements with ease Find elements by multiple attributes Native support for Page and Control model Supports AJAX website testing Supports creating screenshots of webpages Supports frames (cross domain) and iframes Handles popup dialogs like alert, confirm, login etc.. Supports HTML dialogs (modal and modeless) Easy to integrate with your favorit (unit) test tool Works with Internet Explorer 6, 7, 8, 9 and FireFox 2 and 3 Can be used with any .Net Language Licensed under the Apache License 2.0 Downloaded more than 120.000 times And since its open source you can add and contribute new features yourself Пример: [Test] public void SearchForWatiNOnGoogle() { using (var browser = new IE("http://www.google.com")) { browser.TextField(Find.ByName("q")).TypeText("WatiN"); browser.Button(Find.ByName("btnG")).Click(); Assert.IsTrue(browser.ContainsText("WatiN")); } } Сам пользуюсь для граббинга.

Код-гольф: Обход двумерного массива змейкой


Задача соревнования:

Имеется двумерный массив N x N. Нужно написать функцию обхода двумерного массив
змейкой от правого ребра. Пример на картинке.





Пример двумерного массива:

На входе имеется следующий массив:

input = [[4, 3, 2, 1], [5, 6, 7, 8], [12, 11, 10, 9], [13, 14, 15, 16]]


На выходе одномерный массив после обхода:

output = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]


Может использоваться любой язык. 

Основное условие: функция должна работать корректно для любого равностороннего двумерного массива. 

Указывать название языка в заголовке ответа и количество символов минифицированной версии функции через запятую.

Победителем станет тот, кто напишет ее за меньшее количество символов. За это о
получает 300 репутации. Победитель определится через 2 недели (12 января). 

Ответ автора не учитывается при выборе победителя. Желаю удачи :)

Таблица лидеров:



execute("ru.stackoverflow.com", 926927);







Победители:

1 место: Haskell - 39 (@АндрейNOP)

2 место: JavaScript - 40 (@Groxan)

3 место: Groovy - 42 (@Nick)

Всем огромное спасибо за участие и интересные решения ;)
    


Ответы

Ответ 1



Haskell, 39 40 42 r=reverse;s[]=[];s(h:t)=r h++s(map r t) https://ideone.com/JYttmr

Ответ 2



Octave, 47 @(l,b=l(1:2:end,:)=fliplr(l(1:2:end,:)))l'(:)'; Try it online! Если разрешить возвращать не вектор-строку, а вектор-столбец, сокращается на 1 символ: Octave, 46 @(l,b=l(1:2:end,:)=fliplr(l(1:2:end,:)))l'(:); Try it online! Прошлый вариант без анонимной функции: Octave, 40 l(1:2:end,:)=fliplr(l(1:2:end,:));l'(:)' Try it online!

Ответ 3



Python, 56 f=lambda l:sum([x[::i%2*2-1]for i,x in enumerate(l)],[]) Спасибо @АндрейNOP за подсказку! Python, 60 f=lambda l:sum([[x[::-1],x][i&1]for i,x in enumerate(l)],[]) https://ideone.com/JPyzUQ (эта версия с лямбда функцией была любезно предоставлена @Let's say Pie) Попытка 2: инвалидирована по причине неправильного оформления - решение должно быть реализовано как функция Python, 49 sum([[x[::-1],x][i&1]for i,x in enumerate(l)],[]) https://ideone.com/WdHq4n PS @КириллМалышев подсказал как можно сократить код еще на 6 символов - спасибо!! Попытка 1: Python, 55 sum([x if i&1 else x[::-1] for i,x in enumerate(l)],[]) https://ideone.com/2hoF3P

Ответ 4



JavaScript, 38 Данный код работает в Firefox, но не работает на V8 (из-за специфичного алгоритма сортировки). Если это неприемлемо, скажите =) f=a=>a.flatMap((x,i)=>x.sort(_=>~i&1)) console.log(f([[4,3,2,1],[5,6,7,8],[12,11,10,9],[13,14,15,16]])); JavaScript, 40 f=a=>a.flatMap((x,i)=>i%2?x:x.reverse()) console.log(f([[4,3,2,1],[5,6,7,8],[12,11,10,9],[13,14,15,16]]));

Ответ 5



JavaScript, 54 f=a=>a.reduce((c,n,i)=>c.concat(i%2?n:n.reverse()),[]) console.log(f([[4, 3, 2, 1], [5, 6, 7, 8], [12, 11, 10, 9], [13, 14, 15, 16]]));

Ответ 6



Python, 62 61 Пока ничего лучше варианта с рекурсией не придумал :) f=lambda m,x:m and(x&1and m.pop(0)or m.pop(0)[::-1])+f(m,x+1) https://ideone.com/i2d0f6

Ответ 7



Python, 43 f=lambda m,a=-1:m and m.pop(0)[::a]+f(m,-a) https://ideone.com/fQVgtD

Ответ 8



F#, 68 let rec f=function|[]->[]|h::t->List.rev h@(t|>List.map List.rev|>f) https://ideone.com/S6PhEZ

Ответ 9



PowerShell, 51 $f={$args[0]|%{if(++$i%2){[Array]::Reverse($_)}$_}} Try it online!

Ответ 10



C#, 62 dynamic s(int[][]a)=>a.SelectMany((x,i)=>i%2>0?x:x.Reverse()); https://repl.it/repls/EmbarrassedHalfCommand

Ответ 11



PHP, 93 91 function f($a){foreach($a as$v)$r[]=$_++&1?$v:array_reverse($v);return array_merge(...$r);} https://ideone.com/XsiPap

Ответ 12



PHP, 138 105 function f($a){foreach($a as$i=>$y){foreach($y as$x=>$w){$r[]=$i&1?$w:array_reverse($y)[$x];}}return $r;} https://3v4l.org/DgDi8

Ответ 13



C#, 188 184 List S(T[,]a){var l=new List();int m=a.GetLength(1);for(int i=0;i=0;j--)l.Add(a[i,j]);else for(int j=0;j

Ответ 14



C#,171 168 163 153 Так как решения через LINQ уже были, то предлагаю вашему вниманию традиционных подход. Минифицированная версия: T[]F(T[,]a){var d=a.GetUpperBound(1);var r=new List();for(int i=0;i<=d;i++){var b=i%2>0;int j=b?0:d;while(j<=d&j>=0){r.Add(a[i,j]);j=b?++j:--j;}}return r.ToArray();} Удобночитаемая: static T[] F(T[,] a) { var d = a.GetUpperBound(1); var r = new List(); for (int i = 0; i <= d; i++) { var b = i % 2 > 0; int j = b ? 0 : d; while (j <= d & j >= 0) { r.Add(a[i, j]); j = b ? ++j : --j;//Трюк, что бы сэкономить символы на if/else } } return r.ToArray(); } Чуть чуть подправил: T[]F(T[,]a){var d=a.GetUpperBound(1);var r=new List();for(int i=0;i<=d;i++){var b=i%2>0;int j=b?0:d;while(j<=d&j>=0){r.Add(a[i,b?j++:j--]);}}return r.ToArray();} Удобночитаемая: T[] F(T[,] a) { var d = a.GetUpperBound(1); var r = new List(); for (int i = 0; i <= d; i++) { var b = i % 2 > 0; int j = b ? 0 : d; while (j <= d & j >= 0) { r.Add(a[i, b ? j++ : j--]); } } return r.ToArray(); } Опять чуть подправил: T[]F(T[,]a){var d=a.GetLength(1)-1;var r=new List();for(int i=0;i<=d;i++){var b=i%2>0;int j=b?0:d;while(j<=d&j>=0)r.Add(a[i,b?j++:j--]);}return r.ToArray();} Удобночитаемая: T[] F(T[,] a) { var d = a.GetLength(1) - 1; var r = new List(); for (int i = 0; i <= d; i++) { var b = i % 2 > 0; int j = b ? 0 : d; while (j <= d & j >= 0) r.Add(a[i, b ? j++ : j--]); } return r.ToArray(); } И еще чуть-чуть убрал все лишнее: T[]F(T[,]a){int d=a.GetLength(1),g=0;var r=new T[d*d];for(int i=0;i0;int j=b?0:d-1;while(j=0)r[g++]=a[i,b?j++:j--];}return r;} Удобночитаемый: T[] F(T[,] a) { int d = a.GetLength(1), g = 0; var r = new T[d * d]; for (int i = 0; i < d; i++) { var b = i % 2 > 0; int j = b ? 0 : d - 1; while (j < d & j >= 0) r[g++] = a[i, b ? j++ : j--]; } return r; }

Ответ 15



Perl 5, 81 sub f{foreach$row(@_){foreach$val(@$row){push@$s,$i++%2?@$val:reverse@$val;}}$s;} Try it online!

Ответ 16



Groovy, 42 f={i=0;it.each{it.reverse i=!i}.flatten()} https://ideone.com/XirvAC

Ответ 17



C, 97 int f(int n,int a[n][n],int*b){for(int i=0;i

Ответ 18



Ruby, 63 def f(a,x)a[0]?(x%2==0?a.shift.reverse: a.shift)+f(a,x+1):[]end https://ideone.com/T0MECG

Как устроена работа буферизации в php?


Существует 2 вида буферов в php:


Cистемный буфер
Пользовательский буфер


Так могут возникнуть проблемы с работой с буфером.
Для работы с системным буфером есть одна единственная функция - flush(). Для работ
с пользовательским буфером остальные функции с приставкой ob_(Функция ob_implicit_flush() - не относится к данному списку, она больше подходит к системному буферу).

Код для демонстрации:


  Внимание использована инструкция в файле .htaccess
  php_value output_buffering off


";
for ($i = 1; $i <= 10; $i++) {
    echo $i.":(".mt_rand(0,1000).") ";
    flush();
    sleep(1);
}
echo "
LVL of buffer: ".ob_get_level()."
"; header("Hello"); session_start(); header("Support title"); echo "

Thx!

"; ?> Этим примером я хотел показать что при использовании только системного буфера м можем выводить кусочками сообщение на экран. Минусом является то что если захотим отправить заголовки или сессию то будет ошибка, т.к после отправки первого сообщения на экран всевозможные заголовки были отправлены и повторная передача заголовков невозможна. Код для демонстрации - 2: Внимание использована инструкция в файле .htaccess php_value output_buffering 4096 "; for ($i = 1; $i <= 10; $i++) { echo $i.":(".mt_rand(0,1000).") "; flush(); sleep(1); } echo "
LVL of buffer: ".ob_get_level()."
"; header("Hello"); session_start(); header("Support title"); echo "

Thx!

"; ?> В этом примере мы не увидим кусочный вывод текста мы увидем его только когда исполнитс весь код. Даже flush() здесь не поможет. Здесь между прочим используется не только системный но и пользовательский буфер. Кстати вылезут ошибочки по поводу заголовков...тут конечно я не могу объяснить ситуацию из-за чего. Я в конце выскажу свои предположения и вопросы...stay here. Код для демонстрации - 3: Внимание использована инструкция в файле .htaccess php_value output_buffering 4096 "; for ($i = 1; $i <= 10; $i++) { echo $i.":(".mt_rand(0,1000).") "; ob_flush(); flush(); sleep(1); } echo "
LVL of buffer: ".ob_get_level()."
"; header("Hello"); session_start(); header("Support title"); echo "

Thx!

"; ?> В этом примере информация будет выводиться кусками. Этот пример как вы могли заметит отличается только дополнительной функцией ob_flush(). Она таки высвобождает накопленны данные в системный буфер. Следом вызывается flush() который информацию выводит прямо в браузер. Первый пример кода тому явное подтверждение. В конце будут отправлены ошибки о заголовках, оно и понятно данные были уже отправлены. Код для демонстрации - 4: Внимание использована инструкция в файле .htaccess php_value output_buffering off "; for ($i = 1; $i <= 10; $i++) { echo $i.":(".mt_rand(0,1000).") "; sleep(1); } echo "
LVL of buffer: ".ob_get_level()."
"; header("Hello"); session_start(); header("Support title"); echo "

Thx!

"; ?> Интересно, а для чего я этот код добавил? Никаких флюшей здесь нет, даже функций которые работают с буфером вывода. Все гораздо проще чем может показаться на первы взгляд. Дело в том что если мы запустим этот код он отработает как надо и выведет ошибки об уже отправленных заголовках...what? Выходит системный буфер не такой и буфер если он вызывает ошибку о заголовках, однако данные выведутся только тогда когда закончится скрипт. Вот так вот. Эпилог: Мои суждения: Т.к существует 2 буфера в php то получается один зависит от другого: чтобы вывест информацию со свежевыпеченного echo, мы должны сначала высвободить информацию с "нашего" буфера в "казенный" php буфер, после этого мы должны вызвать операцию для "казенного" php буфера на вывод. Системный буфер нельзя удалить, а пользовательский можно. Информация всегда будет попадать в системный буфер хочешь ты того или нет! Пользовательский буфер - это станция где люди ожидают свой самолет. Системный буфе - это самолет, который вмещает в себя людей. Заголовки - это купленные билеты (Вы ж не можете купить билет уже в самолете (Ну по крайней мере я ни разу такого не видел)) если самолет имеет время ожидания вы можете купить билет прямо на станции. Одно без другого жить не может, нет пользовательского буфера - нет времени для покупки билета, нет системного буфера - нет самолета. Выводом буфера будет являться то, когда самолет прибудет на станцию и высадит пассажиров Вопросы: Мой второй пример, мне не понятно по какой причине мне в конце работы скрипта ph пишет ошибку, по факту flush() не срабатывает ввиду того что мы находимся в пользовательском буфере. (Хотя возможно то что в системном буфере ничего нет и этим вызовом мы уже отправляем заголовки) Если опять же во втором примере мы заменим flush() => ob_flush(), мы получим ошибк об отправленных заголовках...why? Как я писал есть 2 буфера системный и пользовательский по логике в системном буфере лежит пользовательский и как вы могли видеть при наличии пользовательского буфера необходимо вызвать 2 флюша, а если у нас имелся только системный то хватало системного флюша. Если мы в файле .htaccess напишем php_value output_buffering on то размер буфер станет unlimited как сообщает php.ini, отнюдь если мы в скрипте вызовем функцию ob_get_level( то получим 0, если вы запускали код, который я писал выше то могли заметить если у нас буфер был заданного размера то мы получали 1, если мы его удаляли или писали off то он у нас был 0 (хочу заметить я знаю что могут быть вложенные буферы и значение может быть куда выше)...собственно почему у нас 0 если размер буфера on? Переосмысление (ответы на первый, второй и третий вопросы): Если данные попадают в системный буфер то заголовки будут отправлены, не важно вызвали вы flush() или нет, как следствие - варн об от отправленных заголовках* Если вы вызовете flush(), но в системный буфер до этого ничего не попадало, это будет означать что данные нужно вывести на экран немедленно из системного буфера, как следствие - отправк заголовков и варн* Если вы пишите ob_flush() в пользовательском буфере с уровнем < 2 (это равносильно тому, что если вы пишите echo в системном буфере), то вы получите варн* так как вы пробрасываете содержимое в системный буфер, а как я писал выше системный буфер если видит какие-то выводимые данные он начинает отправлять заголовки, в этом случае происходит то же самое. Если мы в файле .htaccess напишем php_value output_buffering on при вызове phpinfo( в php файле мы увидим в колонке Local Value - on. В итоге у нас буфер 0! Как так? Вс просто. Эта директива (output_buffering) не поддерживает такое значение. У этой директив есть несколько состояний: 0 - выключенный буфер, 1 - безлимитный буфер и любое число. Кроме того если мы напишем текст вроде: on, off, true, false, sampletext - то в Local Value мы увидим подобные значения, но они будут интерпетироваться как 0. Если мы будем писать текст через пробел то получим ошибку 500, например php_value output_buffering hello world. * Если вы отправляете заголовки после. ** php_value, php_flag, output_buffering - директивы. P.S Возможно я что-то не правильно понял, не стоит воспринимать это как истину, любом случае я смог понять только так, по-другому объяснить свои вопросы не удалось. В любом случае я жду объяснения от людей, которые соображают


Ответы

Ответ 1



Отвечу только на часть вашего вопроса, по крайней мере на текущий момент. Ваш код: "; for ($i = 1; $i <= 10; $i++) { echo $i.":(".mt_rand(0,1000).") "; sleep(1); } echo "
LVL of buffer: ".ob_get_level()."
"; header("Hello"); session_start(); header("Support title"); echo "

Thx!

"; ?> Ваш комментарий к нему: Интересно, а для чего я этот код добавил? Никаких флюшей здесь нет, даже функций, которые работают с буфером вывода. Все гораздо проще чем может показаться на первый взгляд. Дело в том что если мы запустим этот код он отработает как надо и выведет ошибки об уже отправленных заголовках...what? Выходит системный буфер не такой и буфер если он вызывает ошибку о заголовках, однако данные выведутся только тогда когда закончится скрипт. Вот так вот. Так вот. Не совсем понятно, почему вы в этом, четвертом примере решили (если я ва верно понял), почему не должно быть предупреждений насчет заголовков. Так или иначе вы в любом случае должны послать их самыми первыми, до посыла всяких там текстовых сообщений через echo - банально потому, что именно такого поведения от веб-сервера ожидают браузеры. И php тут просто уведомляет вас, что вы неверно пытаетесь сообщить браузеру что-либо через заголовки. Для понимания этого стоит обратиться к собственно протоколу HTTP, а именно к ег структуре. Ответ веб-сервера в общем случае состоит из трех частей: Starting line, Headers и Message Body. Первое отвечает за тип сообщения, второе за заголовки и третье собственно за то, что получит браузер в качестве ответа. Соответственно, когда приведенный код выше начинает выполняться, вне зависимост от того, играетесь ли вы с буфером или нет, веб-сервер, который работает с вашим интерпретаторо php, будь он apache'ем или чем угодно еще, при получении от php первого же текстовог сообщения в третьей строке вашего кода (а оно будет первым, данный код же будет выполняться последовательно), сформирует ответ браузеру, который будет состоять из трех перечисленных выше частей. И далее веб-браузер может всего лишь досылать что-либо в Message Body, но заголовки уже будут отправлены. Другими словами, если вы хотите что-то вывести в заголовках, то вам нужно сформироват их до первого любого вывода в браузер, или же выстроить логику своего приложения каким-либо иным образом, где функция header() не будет использоваться после любой функции, реализующей вывод чего бы то ни было браузеру посредством HTTP.

Ответ 2



Чтобы немного прояснить для себя и других, как работает буферизация в PHP, давайте заглянем в исходники. У меня исходники php-7.1.3. Заходим в корневую папку исходников и пытаемся найти в них реализацию функции ob_get_contents: $ fgrep -r 'PHP_FUNCTION(ob_get_contents)' . Находится такое: ./main/output.c:PHP_FUNCTION(ob_get_contents) Вот эта функция: PHP_FUNCTION(ob_get_contents) { if (zend_parse_parameters_none() == FAILURE) { return; } if (php_output_get_contents(return_value) == FAILURE) { RETURN_FALSE; } } (Попутно было замечено, что PHP-еретики в своих кодах используют табулятор вместо 4-х пробелов.) В том же файле находим функцию php_output_get_contents: PHPAPI int php_output_get_contents(zval *p) { if (OG(active)) { ZVAL_STRINGL(p, OG(active)->buffer.data, OG(active)->buffer.used); return SUCCESS; } else { ZVAL_NULL(p); return FAILURE; } } Что происходит? Выглядит так, как будто что-то (видимо, возвращаемая строка) формируетс на основе некоторого адреса в памяти и длины данных. То есть пользовательский буфер это просто кусок памяти. Вероятно, там еще есть стек меток для каждого вызова ob_start. Это уже сами ищите реализацию ob_start, кому интересно. Аналогичным образом можно найти реализацию функции flush и увидеть, что она работает через функцию конкретного применяемого SAPI. Я посмотрел SAPI для PHP как модуля Apache: static void php_apache_sapi_flush(void *server_context) { php_struct *ctx; request_rec *r; ctx = server_context; /* If we haven't registered a server_context yet, * then don't bother flushing. */ if (!server_context) { return; } r = ctx->r; sapi_send_headers(); r->status = SG(sapi_headers).http_response_code; SG(headers_sent) = 1; if (ap_rflush(r) < 0 || r->connection->aborted) { php_handle_aborted_connection(); } } Здесь используются структуры C API самого Apache, то есть и вывод предварительн должен делаться таким же образом. Как Apache буферизует выводимые данные - это уже его личное дело и к PHP не имеет никакого отношения. Результат таков, что "системная" и "пользовательская" буферизация в PHP - это абсолютн разные вещи, настолько разные, что их не стоило бы называть в одном списке, чтобы не вносить сумятицу, и, возможно, с той же целью, пользовательскую буферизацию вообще стоило бы назвать как-то по-другому.

Javascript: безопасны ли висячие запятые?


Один мой коллега очень любит при определении константных Javascript-объектов оставлять висячие запятые:

var myObj = {
    prop1: 'smth',
    prop2: 'smthElse', // <--- Висячая запятая!
}


Мотивирует он это тем, что при исправлении такого объекта программист получит более релевантный вывод в диффе (1 строчку вместо двух). 

Мне такой стиль очень не нравится, но по причинам также чисто эстетическим:
1. JSLint на такое ругается
2. Неподдержка в старых IE (8-). Но под них мы все равно не пишем.

Стандарт вообще говоря такое разрешает.

Внимание, вопрос: известны ли вам еще причины по которым висячие запятые стоит/н
стоит использовать? Например положительное/отрицательное влияние на сборщики-оптимизаторы или что-нибудь в этом духе
    


Ответы

Ответ 1



Строго говоря: стандарт не запрещает использование висячих запятых в инициализаторе объекта ObjectLiteral[Yield] : { } { PropertyDefinitionList[?Yield] } { PropertyDefinitionList[?Yield] , } Таким образом ставить или не ставить запятую целиком на совести разработчика. В итоге, все решается договоренностью внутри группы разработчиков, чтобы код бы однообразным. Так как это допустимый вариант инициализации - никаких влияний на оптимизацию(минимизацию) оказываться не должно, но зависит от реализации оптимизатора/минификатора. Что касается проверяльщиков: все зависит от правил, которые довольно гибко могу настраиваться. UPD: июль 2017 Добавлю, что, на июль 2017 года, один из самых популярных стайлгайдов - стайлгайд от airbnb не просто не запрещает висячие запятые, а требует их.

Как программно откомпилировать класс и вызвать его метод?


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


Ответы

Ответ 1



[c:\temp\code.cs] using System; using System.Linq; class Test { static public int Run(params int[] values) { return values.Sum(); // эта строка часто меняется } } Этот код компилирует code.cs, вызывает метод Test.Run и выводит результат. using System; using Microsoft.CSharp; using System.CodeDom.Compiler; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { var code = System.IO.File.ReadAllText(@"c:\test\code.cs"); var cps = new CompilerParameters() { GenerateInMemory = true, GenerateExecutable = false }; cps.ReferencedAssemblies.AddRange(new[] { "System.dll", "System.Core.dll" }); var cp = new CSharpCodeProvider(); var cr = cp.CompileAssemblyFromSource(cps, code); foreach (var err in cr.Errors) Console.WriteLine(err); if (!cr.Errors.HasErrors) { var m = cr.CompiledAssembly.GetType("Test").GetMethod("Run"); var res = m.Invoke(null, new object[] { new[] { 1, 2, 3 } }); Console.WriteLine(res); } } } } [Output] 6 Если значение var code указано прямо в Main, например: var code = @" using System; // ... "; то в code надо убрать \r\n var cr = cp.CompileAssemblyFromSource(cps, Regex.Replace(code, "[\r\n]", " ")); иначе при компиляции будет ошибка: A namespace cannot directly contain members such as fields or methods.

Ответ 2



Добавлю, что при использовании сборок не из GAC нужно помещать скомпилированую сборку в каталог с используемыми DLL static void ДобавитьссылкинаDLLВКаталоге(Type тип, System.Collections.Specialized.StringCollection ReferencedAssemblies) { string ИмяФайлаСборки = тип.Assembly.Location; string Каталог = Path.GetDirectoryName(ИмяФайлаСборки); string ИмяФайла = Path.GetFileName(ИмяФайлаСборки); string[] files = Directory.GetFiles(Каталог, "*.dll"); // Console.WriteLine("Всего файлов {0}.", files.Length); foreach (string f in files) { if (! String.Equals(f, ИмяФайлаСборки, System.StringComparison.CurrentCultureIgnoreCase)) if (ReferencedAssemblies.IndexOf(f) == -1) ReferencedAssemblies.Add(f); } } static CompilerResults СкомпилироватьОбертку(string строкаКласса,string ИмяКласса) { bool ЭтоСборкаГак = typeof(T).Assembly.GlobalAssemblyCache; string Путь = Path.GetDirectoryName(typeof(T).Assembly.Location); string OutputAssembly = Path.Combine(Путь, ИмяКласса) + ".dll"; var compiler = new CSharpCodeProvider(); var parameters = new CompilerParameters(); parameters.ReferencedAssemblies.Add("System.dll"); parameters.ReferencedAssemblies.Add("System.Core.dll"); parameters.ReferencedAssemblies.Add("Microsoft.CSharp.dll"); parameters.ReferencedAssemblies.Add("System.Windows.Forms.dll"); parameters.ReferencedAssemblies.Add(typeof(AutoWrap).Assembly.Location); if (!ЭтоСборкаГак) { parameters.ReferencedAssemblies.Add(typeof(T).Assembly.Location); ДобавитьссылкинаDLLВКаталоге(typeof(T), parameters.ReferencedAssemblies); } else { string ИмяСборки = typeof(T).Assembly.ManifestModule.Name; if (parameters.ReferencedAssemblies.IndexOf(ИмяСборки) == -1) parameters.ReferencedAssemblies.Add(ИмяСборки); } if (ЭтоСборкаГак) parameters.GenerateInMemory = true; else { // parameters.GenerateInMemory = true; parameters.GenerateInMemory = false; parameters.OutputAssembly = OutputAssembly; } parameters.GenerateExecutable = false; parameters.IncludeDebugInformation = true; var res = compiler.CompileAssemblyFromSource(parameters, строкаКласса); return res; } Кроме того можно использовать Scripting-API String ПолучитьСтрокуДелегата() { string returnStr = @"return (MatchEvaluator)((match) => { string x = match.Value; // If the first char is lower case... if (char.IsLower(x[0])) { // Capitalize it. return char.ToUpper(x[0]) + x.Substring(1, x.Length - 1); } return x; });"; return returnStr; } private void button_Click(object sender, RoutedEventArgs e) { // textBoxEval.AppendText(((D)СоздатьДелегат())().ToString()); string words = "надо заменить все первые буквы в словах на заглавные"; string pattern = @"\w+"; // MatchEvaluator evaluator = (MatchEvaluator)ПолучитьДелегат(); var scr = Microsoft.CodeAnalysis.Scripting.ScriptOptions.Default .WithReferences(typeof(MatchEvaluator).Assembly) .WithImports("System", "System.Text.RegularExpressions"); var result = Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.EvaluateAsync(ПолучитьСтрокуДелегата(), scr).Result; MatchEvaluator evaluator = (MatchEvaluator)result; textBoxEval.AppendText(Regex.Replace(words, pattern, evaluator)); } Или более сложный вариант string ПолучитьСтрокуКласса() { var res = @" class ClassTest { string строка; public Func ЗаданнаяСтрока; public ClassTest(string Строка) { строка = Строка; ЗаданнаяСтрока = () => строка; } public static Func ПолучитьДелегатОбъекта(string Строка) { var obj = new ClassTest(Строка); return obj.ЗаданнаяСтрока; } } return new Func>(ClassTest.ПолучитьДелегатОбъекта); "; return res; } private void button2_Click(object sender, RoutedEventArgs e) { var scr = Microsoft.CodeAnalysis.Scripting.ScriptOptions.Default .WithImports("System"); var result = Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.EvaluateAsync(ПолучитьСтрокуКласса(), scr).Result; var делегат = (Func>)result; textBoxEval.AppendText(делегат("Тестовая строка 2")() + Environment.NewLine); } Кроме того можно использовать DynamicMethod Для рефлектора есть дизассемблер в Reflection.Emit

Зачем при записи чисел в конце добавляют .0?


Часто в коде программ числа записываются в виде цифры с .0 на конце. Например:

C: double s = 1.0 / 6.0;

Delphi: var d: Single; begin d := 16.0 + 1.0; end;

JS: var t = 90.0;

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

Какой смысл несет добавление .0 в данных случаях?
В каких случаях .0 можно опускать, а в каких он меняет логику поведения программы?
    


Ответы

Ответ 1



Если говорить в общем, то добавление .0 к числу, изменяет его тип и превращает ег из целого, в дробное (число с плавающей точкой). Такое изменение типа числа влияет на то, как оно будет храниться в памяти и на то, как и какие операции с ним могут выполняться. В JS добавление .0 абсолютно лишено всякого смысла, т.к. там все числа изначально являются дробными. В Delphi так же не возникает необходимости вручную приводить целые числа к дробным т.к. компилятор замечательно справляется с этим самостоятельно, там где это нужно. Операци деления двух целых всегда возвращает дробное число и его невозможно по ошибке присвоить целому. Для особых случаев, когда надо выполнить целочисленное деление, есть специальный оператор div. А вот в Си и некоторых других языках, и для целочисленного, и для обычного делени используется один и тот же оператор деления / который ведёт себя по разному, в зависимости от типа операндов: если оба операнда целые, то и в результате деления будет целое (целочисленное деление) double i = 5 / 2; даст 2.0 (дробная часть результата операции отбрасывается, получается целое число, которое затем приводится к целевому типу double). если хоть один из операндов является дробным, то и результат будет дробным: double i = 5 / 2.0; даст 2.5. В Си, вместо .0 перед числом можно указывать тип числа с плавающей точкой (float/double) т.е. выполнять приведение типа: double i = 5 / (double) 2; такая конструкция используется, если операнд не число, а переменная целочисленного типа: int k = 2; double i = 5 / (double) k; // --> i = 2.5

Ответ 2



В конце литерала нужно добавлять .0 в том случае, если вам важно получить тип с плавающе точкой. Я приведу пример для С/С++, но он также будет актуальным для Java, C# и многих других языков: double X = 15 / 2; // Выведет 7 Здесь тип переменной - double, так что можно подумать, что в ответе получится 7.5 но это не так. Поскольку и 15 и 2 не имеют дробной части, они будут считаться целым числами при вычислении, и для них сначала будет произведено целочисленное деление (15 / 2 = 7), а потом полученный результат будет конвертирован и записан в переменную типа double. Это можно исправить, добавив пустую дробную часть к одному или к обоим литералам Если один из операндов является числом с плавающей точкой, то второй операнд тоже будет конвертирован в число с плавающей точкой, и вы получите правильный результат: double X = 15.0 / 2; // Выведет 7.5 Общее правило для C++ и подобных языков: добавляйте .0 к литералам тогда, когда ожидаете ответ типа float или double. Это поможет избежать неожиданностей.

Ответ 3



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

Ответ 4



В давние времена, когда появился Fortran - первый язык программирования высокого уровня, имеющий транслятор, это были азы, с которых начиналось обучение программированию. Добавление точки после числа сейчас называют неявным приведением типа. Когда надо было получить тип REAL от деления целых, писали Z=(X+0.)/(Y+0.) или Z=7./8. Это, конечно, было неисчерпаемым источником багов в программах. Тогда считалось несолидным добавлять ноль после точки. Это же целая лишняя дырка в перфокарте!

Зачем нужен upcast (повышающее приведение типа)?


Недавно обсуждалось, зачем нужен downcast — приведение типа от более общего к боле
конкретному. А нужен ли upcast (повышающее приведение) — явное приведение типов в обратную сторону, от более конкретного к более общему? Ведь мы ничего не теряем, работая с более конкретным объектом?
    


Ответы

Ответ 1



Для начала, общая причина, которая касается не только C#, но и большинства объектно-ориентированны языков: семантика. Если у программиста есть объект конкретного типа, он тем не менее может хотеть работать с ним как с более общим объектом: programming against an interface, not implementation! Это позволяет убедиться, что в коде не используются лишние, конкретные свойства что будет мешать в будущем обобщить код. Разумеется, обычно это слишком строгая цель, и без этого можно обойтись. Следующая причина — выбор перегрузки, неполиморфного метода. В зависимости от статическог типа объекта (при совпадающем динамическом типе) могут быть вызваны различные перегрузки при одинаково выглядящем коде. Примеры: Вызов нужной перегрузки: void f(object o) { Console.WriteLine("обрабатываем объект"); } void f(string s) { f((object)s); // избегаем рекурсии Console.WriteLine("дополнительная обработка для строки"); } string s = "Пушкин"; f(s); // вызывает перегрузку со строкой f((object)s); // вызывает перегрузку с объектом Ещё один пример, который часто встречается в коде: class X { public static bool operator == (X x1, X x2) { // оптимизация: проверим совпадение объектов if ((object)x1 == (object)x2) return true; // далее более дорогая проверка равенства по свойствам } } Вызов закрытого метода: class Base { public void X() { Console.WriteLine("нужный метод"); } } class Derived : Base { public new void X() { Console.WriteLine("бесполезный метод"); } } Derived d = new Derived(); ((Base)d).X(); Явная имплементация интерфейса не позволяет вызвать метод по имени. class X : IDisposable { void IDisposable.Dispose() {} } var x = new X(); // ... ((IDisposable)x).Dispose(); // по-другому не вызвать В случаях, когда тип переменной выводится неявно из типа другой переменной, бывают случаи, когда нас не устраивает автоматически выведенный тип. Пример: var list = new[] { 1, 2 }.ToList(); list.Add("ой"); Мы хотим получить список object'ов, но выведение типов даёт нам список int'ов. Мы можем написать var list = new[] { (object)1, 2 }.ToList(); list.Add("ой"); так всё будет компилироваться. Ещё один тесно связанный случай — тернарный оператор. Если типы альтернатив различны, компилятор не может найти общий тип выражения, и приходится помогать: Animal animal = nya ? new Cat() : new Dog(); // не компилируется Animal animal = nya ? (Animal)new Cat() : new Dog(); // компилируется (Очень похожая проблема возникает с Nullable-типами: int? result = good ? 1 : null требует явного преобразования одного из операндов-альтернатив.) Этот случай подсказал @Pavel Mayorov в комментариях, спасибо! Ещё одно применение — неявная упаковка (boxing). Например, функции типа GetEnumerator( могут вернуть объект типа-значения, который реализует интерфейс IEnumerator. Работать с ним не всегда удобно: static public IEnumerable MultiZip( this IEnumerable> sequences, Func, R> resultSelector) { var enumerators = sequences.Select(s => (IEnumerator)s.GetEnumerator()).ToList(); try { while (enumerators.All(en => en.MoveNext())) yield return resultSelector(enumerators.Select(en => en.Current)); } finally { foreach (var en in enumerators) en.Dispose(); } } Если бы мы забыли upcast к IEnumerator, то в enumerators мог бы оказаться набо value type (и это так и есть в нашем случае!). При этом, поскольку мы мутируем наши энумераторы (MoveNext), то для случая value type мы бы вызывали этот метод на копии значения, и таким образом код бы не сработал.

Что такое маршалинг?


Как бы вы объяснили "на пальцах", или с примером кода что это такое?
Вот сериализация - это ясно, это копирование структур из-в в простой форме, наприме
в форме байтового потока. И вот утверждается, что в этом случае сериализация по своей сути является маршалингом так как реализует "pass-by-value semantics". Что думаете?
    


Ответы

Ответ 1



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

Ответ 2



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

Анимация пешеходных и других маршрутов на растровой карте


Возникла идея, как сделать анимацию похода с семьей в ТЮЗ г. Санкт-Петербурга, допустим для жителей Ленинградской области. Чтобы реализовать анимацию нескольких маршрутов.

Маршрут движения будет на поезде до вокзала и  пешком собственно до театра. 

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


Анимация поезда,- составленного из символов юникода, который движется вдоль заданного пути, проложенного на растровой карте. 
Анимация рисования пешеходного маршрута от вокзала до ТЮЗ'а.
Анимация фигурок взрослых и их детей.
Анимация движения персонажей вдоль заданного маршрута.  


Ниже пример одной части анимации - прокладка пешеходного маршрута  




  
  
  
 
  
  
  






Как сделать, перечисленные выше, виды анимации?
    


Ответы

Ответ 1



Буду поэтапно, подробно объяснять все фрагменты анимации. Отдельные куски могут быт использованы для оживления раздела "Контакты" сайта заказчика. Опираясь на описание можно добавить собственные карты и проложить необходимы маршруты. Добавление растровой карты в SVG Для внедрения выбранного фрагмента растровой карты в SVG используется тег Прорисовка маршрутов движения поезда и людей Загружаем карту в векторный редактор и инструментом "Рисовать кривые Безье" добавляем узловые точки на маршруты движения. Сохраняем файл в формате *.svg и копируем из него патчи маршрутов Вычисляем длину маршрутов: для поезда это будет - 312px пешеходный маршрут - 409px Пишем команду анимации для пешеходного маршрута Анимация поезда,- составленного из символов юникода Юникод вагона - 🚃 Перемычки между вагонами - - Поезд будет идти по маршруту: Ниже код поезда без анимации: 🚂 - 🚃 - 🚃 - 🚃 - 🚃 - 🚃 - 🚂 Команда анимации движения поезда здесь будет несколько иная в отличии от анимации пешеходного маршрута, так как необходимо запустить по одному маршруту несколько вагонов. ` Ниже код анимации поезда: 🚃 - - 🚃 - 🚃 - 🚃 - 🚃 - 🚃 Анимация фигурок взрослых и их детей Анимация движения людей основана на анимации параметра "d" нескольких для фигур человека в нескольких фазах ходьбы, что при быстрой смене значений "d" создаёт иллюзию ходьбы. Ниже код анимации: Собираем все фрагменты анимации вместе Тайминг в SMIL SVG анимации просчитывается и реализуется довольно просто, в отличии от анимации CSS Если нужно запустить вторую анимацию строго после окончания первой анимации, то применяется следующая запись: begin="an1.end", где an1 - уникальный идентификатор первой анимации. Если вторая анимация должна запуститься через 1 сек. после окончания первой анимации, то - begin="an1.end+1s" Возможна и такая форма записи - begin="anTrain.end-7.5s",что означает запуск другой анимации за 7.5 сек. до окончания анимации с ID - anTrain Используя эти возможности можно очень гибко согласовывать взаимодействие отдельны фрагментов анимации и в дальнейшем, что-то изменить, подстроить, не составит большого труда. Ниже полный код всей анимации .container { width:100%; height:100%; } .play-audio { position:absolute; top:0%; left:50%; }
🚅 - 🚃 - 🚃 - 🚃 - 🚃 - 🚃 - 🚂
Приложение адаптивно и работает во всех современных браузерах, кроме IE Update 03.04.2019 г. Добавлен аудиоплейер. Использован трек: группы Машина времени - Разговор в поезде

& после объявления функции в классе


class A
{
public:
    void f() &;
};


Что означает & после объявления функции?
    


Ответы

Ответ 1



Это означает, что функция может быть вызвана для lvalue объекта класса A. Рассмотрите следующий пример #include class A { public: void f() & { std::cout << "A::f() &" << std::endl; } void f() && { std::cout << "A::f() &&" << std::endl; } }; int main() { A a; a.f(); A().f(); } Вывод на консоль этой программы A::f() & A::f() && В классе A функция f перегружена для lvale объектов класса A и для rvalue объектов класса A. В этой программе в предложении a.f(); вызывается функция f для lvalue объекта a. В этом же предложении A().f(); вызывается функция f для rvalue объекта A(). Другой пример. #include class A { public: void f() const & { std::cout << "A::f() const &" << std::endl; } void f() & { std::cout << "A::f() &" << std::endl; } }; void h( const A &a ) { a.f(); } int main() { A a; a.f(); h( a ); } Вывод на консоль A::f() & A::f() const & В этом примере класс A объявляет две перегруженные функции с именем f: одна для константных lvalue объектов, а другая для неконстантных lvalue объектов. В предложении a.f(); вызывается функция для неконстантных объектов, так как объект a не является константным. Однако внутри функции h h( a ); вызывается перегруженная функция для константных объектов, так как параметр функции является константой ссылкой. Еще один пример. Данная программа не будет компилироваться #include class A { public: void f() & { std::cout << "A::f() &" << std::endl; } }; int main() { A().f(); } Потому что rvalue не может быть привязана к неконстантной ссылке. Однако если объявить функцию как имеющую дело с константной ссылкой, то программа будет компилироваться #include class A { public: void f() const & { std::cout << "A::f() const &" << std::endl; } }; int main() { A().f(); } Чтобы было более понятно, то можно представить эти функции следующим образом. Каждая нестатическая функция-член класса имеет неявный параметр, который соответствует this Поэтому данные две функции-члены класса void f() const & и void f() & концептуально выглядят как void f( A * const & ) /*const &*/ и void f( A * & ) /*&*/ Поэтому последнюю функцию нельзя вызывать для временных объектов типа A *, тогд как первую - можно, так как константная ссылка может привязыватья к временному объекту. Вот пример, который упрощенно демонстрирует сказанное #include void f( int * const & ) { std::cout << "f( int * const & ) " << std::endl; } void f( int * & ) { std::cout << "f( int * & )" << std::endl; } int main() { int i = 10; int *pi = &i; f( pi ); f( &i ); } Вывод на консоль: f( int * & ) f( int * const & )

Анимация персонажа улитки


У меня есть, вот такая картинка симпатичного семейства улиток - 🐌 



Хочется сделать анимацию движения улиток, плюс к этому анимацию тела улитки, её усиков, глаз. 
  Понятно, что нужно искать или рисовать улитку в формате svg. В инете достаточно много файлов svg с улитками.
  Но мне понравилась именно эта картинка.  

Если есть svg файл большой улитки, то сделать уменьшенные копии - детёнышей не составит труда, используя команду scale(0.25):

  
`    


Анимация перемещения вдоль пути может быть реализована командой:


   
  


Как сделать остальную анимацию частей тела улиток?  
    


Ответы

Ответ 1



Анимация подошвы улитки Если есть необходимость подробно разобраться с техникой анимации атрибута "d" команды Path смотрите здесь Вся последующая реализация анимаций частей тела улитки основана именно на этой технике. Чтобы анимировать тело улитки необходимо найти патч, который рисует внешний контур подошвы улитки. Загружаем файл в векторный редактор и делаем копию этого патча. Сдвигаем контрольны узлы на копии патча, чтобы имитировать изменение контура подошвы улитки. Сохраняем фай и копируем эти патчи. Чтобы получилась анимация необходимо сделать плавный переход от первоначального патча ко второму и обратно. Для этого надо использовать анимацию атрибута патча “d” Анимация глаз и рта улитки Глаза улитки прикреплены к усикам, которые движутся, поэтому для более реалистичной картины необходимо обеспечить движение глаз влево-вправо и вверх-вниз. Для этого используется команда : Анимация рта улитки реализуется плавным изменением параметра “d” патча. Другими словам верхняя губа изгибается, имитируя открывание рта, а затем смыкается с нижней губой, как-бы, закрывая рот. Создание маленьких улиток Для создания детенышей улиток будем использовать команду для клонирования и команду scale(0.25) для уменьшения Анимация движения улиток Для анимации движения улиток используется команда движение по заданному пути Далее оформляем ландшафт, добавляем градиенты и музыку для создания настроения. Update 19.03.2019 г. Chrome теперь не позволяет автоматически запускать файлы *.mp3, поэтому пришлось добавить аудиоплайер. Firefox воспроизводит в автоматическом режиме файлы *.mp3 Добавлена анимация глаз улитки Добавлена анимация солнечных лучей .container { width:90%; height:90%; }
Использована песня: Bobby McFerrin don't worry be happy