Страницы

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

четверг, 1 ноября 2018 г.

Деление с округлением в большую сторону

Math.Round округляет по правилу, как заставить его округлять в большую сторону, или какую другую функцию использовать?


Ответ

Math.Ceiling - к большему целому Math.Floor - к меньшему целому

Олимпиадная задача про зайца

Один мой знакомый занимается составлением олимпиадных задач, которые впоследствии иногда дают на собеседованиях. Возникли затруднения с решением данной задачи:
В нашем зоопарке появился заяц. Его поместили в клетку, и чтобы ему не было скучно, директор зоопарка распорядился поставить в его клетке лесенку. Теперь наш зайчик может прыгать по лесенке вверх, перепрыгивая через ступеньки. Лестница имеет определенное количество ступенек N. Заяц может одним прыжком преодолеть не более К ступенек. Для разнообразия зайчик пытается каждый раз найти новый путь к вершине лестницы. Директору любопытно, сколько различных способов есть у зайца добраться до вершины лестницы при заданных значениях K и N. Помогите директору написать программу, которая поможет вычислить это количество. Например, если K=3 и N=4, то существуют следующие маршруты: 1+1+1+1, 1+1+2, 1+2+1, 2+1+1, 2+2, 1+3, 3+1. Т.е. при данных значениях у зайца всего 7 различных маршрутов добраться до вершины лестницы.
Хотелось бы узнать, что предложите вы. Мне интересен сам алгоритм, код далеко не так важен, но в любом случае это будет только плюсом =)


Ответ

Простейшая динамика (в которой я очень слаб, к сожалению) Заведем массив dp размерностью n+1 (каждый i-й элемент будет хранить количество если бы было i ступенек) dp[0]=1 //ступенек нет Остальные рассчитываются как сумма всех предыдущих на расстоянии не больших k Ответ в dp[n]. upd тоже код с acmp.ru a[0] = BigInteger.ONE; int start; for (int i=1; i<=n; i++) { start = Math.max(0, i-k); a[i] = BigInteger.ZERO; for (int j=start; j

Запуск процесса из потока

Подскажите, пожалуйста, как можно запустить процесс не блокируя основной поток, но и получить уведомление о завершении запущенного процесса?


Ответ

Необходимости в отдельном потоке нет.
Примерно так:
Process process = new Process(); process.StartInfo.FileName = "myprocess.exe"; //подписываемся на событие завершения процесса и включаем уведомления process.Exited += process_Exited; process.EnableRaisingEvents = true; //асинхронная операция, не блокирует поток, из которого была вызвана process.Start();
Где process_Exited — это ваш обработчик завершения процесса.
Если по каким-то причинам завершения процесса нужно дождаться синхронно, то после Start() нужно вызвать метод WaitForExit()

Нитка и зажигалка. (Две нитки и зажигалка - 2).

А вот вам посложнее: После неудачного эксперимента из предыдущей задачи у нас осталась только одна нитка. Горит она, по-прежнему, час. Надо отмерить 15 минут. :)
Призов не полагается. Тока палец вверх всем правильно ответившим, галочка первому, 20 баллов от меня лично, если будет представлено строгое доказательство.
UPD:Спасибо @Asen2 за комментарий. Задача имеет решение, по крайней мере, на уровне алгоритма. На всякий случай, замечу, что неравномерность горения конечна. То есть мгновенно 99,9999% нитки сгореть не могут, оставив для оставшегося 0,0001% весь час.
UPD: скорость горения изменяется достаточно плавно.
UPD: решение уже опубликовано. Оно лучше, чем то, которое хотел предложить я. Поэтому Чуток баллов ушло и @nike. Жду строгого доказательства. (Я уже сделал, поэтому и наградил найка)


Ответ

поджигаем нитку в начале, в конце и примерно по центру. Ждем пока выгорит одна часть и поджигаем по центру нитку которая еще горит, и так до тех пор пока будет гореть нитка, главное чтоб нитка все время горела в 4 частях, после того как все части сгорят пройдет 15мин

Несколько конструкторов с разным количеством аргументов

Можно ли в Python в одном классе делать несколько конструкторов с разным количеством аргументов? class a: def __init__(self,b): pass def __init__(self): pass В данном случае можно создавать экземпляры класса только со вторым констуктором, в то время как запись x = a(42) вызовет ошибку.


Ответ

Если быть кратким то НЕТ.
Более детально посмотрите обсуждение вот ЗДЕСЬ. Там и варианты решений есть такие как использование необязательных или ключевых аргументов.

GetHashCode — вычисление хэш

Мне интересно узнать, каким образом формируется хэш объекта в C#. Например, есть тестовый класс:
public class A { public int b = 0; public int c; }
Далее где-нибудь в коде:
A a1 = new A(); A a2 = new A(); a1.c = 10; a2.c = 10;
a1 и a2, по логике вещей, совершенно идентичны. Но для C# нет. Их хэши различны. Конечно же, понятно, что это различие может быть оправдано, например, различием адресами в памяти, ну или временем создания, в конце концов :). Так вот хочется точно узнать, как же формируются хэши объектов и чем обосновывается их различие для идентичных пользовательских объектов.


Ответ

Для начала, поскольку ваш класс A не переопределяет GetHashCode(), эта функция наследуется от object
Точный алгоритм вычисления GetHashCode() для object не специфицирован
Реализация GetHashCode по умолчанию не гарантирует уникальные значения для различных объектов. Кроме того, .NET Framework не гарантирует, что реализация GetHashCode по умолчанию, в том числе значения, возвращаемые ей, не поменяются при смене версии фреймворка. Соответственно, значение функции GetHashCode по умолчанию не должно быть использована как уникальный идентификатор объекта с целью хеширования.
То, что вам два объекта кажутся идентичными, не гарантирует, что у них при реализации по умолчанию совпадут хеш-коды. С другой стороны, посудите сами: если у двух людей совпадает имя и фамилия, это ведь ещё не значит, что это один и тот же человек? Так и .NET framework, если у двух экземпляров класса A совпадают все поля, ещё не считает эти экземпляры одним и тем же объектом. Соответственно, по умолчанию используется что угодно -- например, адрес при создании или время создания, документация не говорит о точном методе. Если вас не устраивает поведение по умолчанию, вы должны его перекрыть.
Если ваш класс не использует операцию Equals и не является ключом в хеш-таблицах, в принципе можно ничего и не делать, вас должно устраивать и поведение по умолчанию. Но если ваш класс служит ключом, вам необходимо переопределить как Equals, так и GetHashCode(), причём согласованным образом: если x.Equals(y) возвращает true, то и хеш-коды у x и y обязаны совпадать.
Отсюда следует, что вы должны переопределять функцию GetHashCode() вместе с функцией Equals. Например, так:
public class A { public int b = 0; public int c;
public override bool Equals(object obj) { return Equals(obj as A); }
public bool Equals(A that) { if (that == null) return false; return this.b == that.b && this.c == that.c; }
public override int GetHashCode() { return b.GetHashCode() * 13 + c.GetHashCode(); } }

В C#, в отличие от многих других языков, есть объекты двух типов: объекты, полностью определяющиеся своими атрибутами (то есть, полями), и объекты, представляющие собой самостоятельную сущность, не сводящуюся лишь к значениям атрибутов.
Примером объекта первого типа являются, например, числа: если вы пишете
x = 5; y = 5;
-- нет никакой разницы между первой и второй пятёркой, это одна и та же сущность. Подобные штуки называются в .NET типами-значениями и вводятся ключевым словом struct. Для них метод Equals по умолчанию сравнивает значения всех полей, и если они равны, заключает, что и структуры равны, одинаковы. Поэтому структуры можно копировать по значению, структуру можно безболезненно заменить на её копию. (Метод GetHashCode по умолчанию определён соответствующим образом: для равных структур он гарантированно возвращает одинаковый хеш.)
Примером другой сущности может служить, например, автомобиль, атрибутами которого являются марка, год выпуска, мощность двигателя и т. п. Два автомобиля с одинаковыми техническими параметрами -- это всё же разные автомобили. Такие объекты в C# называются ссылочными типами и вводятся ключевым словом class. Для классов, соответственно, реализация Equals по умолчанию не имеет права предполагать, что одинаковые значения полей автоматически означают равенство, каждый экземпляр класса считается уникальным
Подумайте, не нужна ли вам на самом деле вместо класса A структура?
Вопрос о том, определяемся ли мы все, люди, лишь нашим набором параметров, или в нас есть что-то вне этого, не так уж прост.

Элемент, “прозрачный” для мыши

Всем привет. Странный вопрос - есть ли возможность сделать элемент "невидимым" для мыши? Поясню, есть блок

, в нем с абсолютным позиционированием. <А> может быть больше или меньше, длиннее или шире
, не важно; нужно, чтобы мышь вообще не замечала этот . Фиддл, красные квадраты - курсор должен быть normal(и клик уходит на либо то, что ниже), зеленые - pointer (и клик уходит на
); text'а быть вообще не должно. Итак, возможно ли и если да, то как? В
могут быть и другие элементы, "поставить прозрачную копию поверх " рассматриваю в последнюю очередь, желательно работать только с . Возможно JS (не jQuery) PS Ноу, ноу хакерз, нужна некликабельная метка на карту, произвольных размеров, которая может залезать на другие регионы и не накрывать их. UPD: для непонимающих) Нужно сделать так, чтобы элемент (div, a - не важно) отображался, но для мыши не существовал: клики проходят насквозь, события onmouse* не происходят, курсор не изменяется. А в фиддле (выше) красные и зеленые квадраты - это положения мыши и реакция на них. Теперь лучше? UPD примерно так оно должно себя вести, но без слоев #overlay1/2


Ответ

Ну что, решил сам. Спасибо, к сожалению, сказать некому)
Таки пришлось попользовать jQuery из-за ленивости перепиливания $.trigger и $.css.
Решение (fiddle)
Алгоритм примерно такой:
вешаем на ссылку все мышиные события в их обработчике скрываем ссылку, получаем нижний элемент с помощью божественной функции document.elementFromPoint и перекидываем событие на него копируем в ссылку стиль курсора нижнего элемента (если он auto - ставим default) показываем ссылку обрываем событие ссылки профит, как говорится
Возможно, кому-то понадобится - штука весьма юзабельна на гео-картах и для разного рода всплывающих подсказок.
ЗЫ: если заморачиваться - курсор надо выставлять в зависимости от типа нижнего элемента - pointer для ссылки, text для текстовых полей и т.д. Но мне, честно, просто было лень, свою задачу я решил)
Дублирую fiddle, вариант для изучения
function passEventLower(e) { e.preventDefault(); e.stopPropagation(); var $el = $(document.elementFromPoint(e.pageX, e.pageY)); var c = $el.css('cursor') || 'default'; if (c == 'auto') c = 'default'; $(e.target).css('cursor', c); $el.trigger(e.type); } $('html *:not(#a1)').on('click', function(e) { console.log(this.tagName, e.type); }); $('#a1').on('mousedown mouseup click mousemove', function(e) { var $a = $(this); $a.hide(); passEventLower(e); $a.show(); return false; }); #d1 { position: absolute; width: 100px; height: 100px; background: #eee; cursor: pointer; overflow: show; } #a1 { display; block; position: absolute; white-space: nowrap; line-height: 30px; margin: 30px 0px 0px 10px; cursor: default; }


Как залить фон button частично?

Привет! Нужно вводить число от 0 до 1 в edittext, а после заливать цветом часть фона button в соответствии с введенным числом( т.е. ввели 0.25 - заливаем четверть кнопки)
В списке аттрибутов ничего подобного, к сожалению, нет


Ответ

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

Реализация проста и состоит из менее десятка строчек - подрисовывать на кнопку дополнительный слой с шириной, которую мы можем установить сами:
"Мучить" будем очень модную сейчас material-кнопку (AppCompatButton) из последней support.v7 - 22.1 , но то же самое можно проделать с любым виджетом, это не принципиально. Собственно сам класс кастомной кнопки ProgressButton.java
public class ProgressButton extends AppCompatButton { private float mRatio; private int mColor = Color.GREEN;
public ProgressButton(Context context) { super(context); init(); }
public ProgressButton(Context context, AttributeSet attrs) { super(context, attrs); init(); }
public ProgressButton(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); }
void init(){
}
public void setRatio (float ratio){ mRatio = ratio; invalidate(); }
public void setColor (int color){ mColor = color; invalidate(); }
@Override protected void onDraw(Canvas canvas) {
Drawable fill = getResources().getDrawable(R.drawable.abc_btn_default_mtrl_shape); fill.setColorFilter( mColor, PorterDuff.Mode.MULTIPLY); fill.setAlpha(128); fill.setBounds(0, 0, (int) (getWidth()*mRatio), getHeight()); fill.draw(canvas); super.onDraw(canvas); } }
в качестве дополнительного слоя мы используем тот же xml (abc_btn_default_mtrl_shape.xml), что используется и для прорисовки бэкграунда самой кнопки, окрашивая его в собственный цвет, но можно использовать и любой собственный вид при необходимости. Прозрачность устанавливается для того, чтобы эффект нажатия был виден по всей кнопке, а не только на не окрашенной части.
кастомная кнопка реализует методы:
setRatio(float ratio) - установить процент заполнения фона кнопки - число от 0 до 1. setColor(Color color) - установить цвет заливки
Теперь, как это все работает. Простой пример заполняет часть кнопки при нажатии на нее:
public class MainActivity extends AppCompatActivity {
ProgressButton button; float ratio;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button = (ProgressButton) findViewById(R.id.progress_button);
}
public void onClicked (View view) {
button.setRatio(ratio); ratio = ratio +0.3f; if (ratio >= 1) ratio = 0; } }
activity_main.xml
android:layout_width="match_parent" android:layout_height="match_parent" android:padding="10dp" android:orientation="vertical">

Можно ли в LocalStorage хранить js/css/html данные?

Возможно ли хранить в LocalStorage хранить js/css/html данные, чтоб при каждой перезагрузке их не тягать?


Ответ

А зчаем изобретать велосипед? Кэш давно реализован во всех браузерах без ухищрений с localStorage. Если URL и хэш файла на сервере не меняются, и вы не шлёте в хедерах явный запрет на кэширование ресурсов, эти ресурсы сохранятся в кэше и при обновлении страницы будут подтягиваться из него. Также кэш, в отличие от localStorage, меньше ограничен в объёме, браузер может закэшировать хоть весь статический контент вашего сайта, в то время как в localStorage помещается всего 2.5-10 МБ данных.

Замена дефолтного редактора кода в git

Начал работать с git (а именно c BitBucket) и встроенный редактор (VIM) оказался для меня весьма непонятным в управлении.
Возникло желание заменить его на другой (хоть на встроенный в OS Windows 7 блокнот). Как это сделать?


Ответ

1. Уровни настройки.
Редактор, который использует Git, выбирается из следующего списка, именно в этом порядке (из man git-commit):
Значение переменной окружения GIT_EDITOR Значение в конфигурации Git core.editor. Используется обычный для Git приоритет настроек:
--local — для данного проекта. --global — для данного пользователя. Логично настраивать редактор именно на этом уровне. --system — для этой рабочей машины (в рамках ОС). Значение переменной окружения VISUAL Значение переменной окружения EDITOR (зачем столько переменных?) Если все эти варианты не установлены, то использует Vi/Vim.
2. Зачем -w?
Зачем в большинстве приведенных команд параметр -w (--wait)? Git открывает редактор и ждет возврата exit code, чтобы продолжить работу на основе данных в файле. Если этого параметра нет, Git будет продолжать работу сразу и не дождется, когда вы отредактируете и сохраните файл.
из atom -h
-w, --wait Wait for window to be closed before returning.
3. Настройка через изменение core.editor
Не требует прямого редактирования файлов и обладает гибкостью в уровнях настройки. Минус в том, что остальные приложения продолжат использовать редактор по умолчанию.
В общем виде настройка конфигурации выглядит так:
git config --global core.editor "editor --parameters"
3.1 Linux, OS X
Многие редакторы (Atom, TextWrangler и другие) не включают поддержку командной строки при установке. Их необходимо включить отдельно, найдя в меню команду Install Shell Commands
Эти строки можно сразу копировать и выполнять в терминале:
Atom
git config --global core.editor "atom -w"
Emacs (-w по умолчанию):
git config --global core.editor "emacs"
Sublime Text
git config --global core.editor "subl -n -w"
Textmate
git config --global core.editor "mate -w"
TextWrangler
git config --global core.editor "edit -w"
Vim (-w по умолчанию):
git config --global core.editor "vim"
3.2 Windows
Вариант 1: добавить редактор в переменную PATH и использовать строки как для *nix.
Некоторые редакторы при установке сами прописывают себя в PATH.
Вариант 2: не меняя переменную PATH, напрямую указать путь к исполняемому файлу. Обратите внимание на вложенность кавычек и на то, как лаконично выглядит параметр --wait под Windows
Notepad++, на 32-битной ОС (источник):
git config --global core.editor \ "'C:/Program Files/Notepad++/notepad++.exe' -multiInst -notabbar -nosession -noPlugin"
Notepad++, на 64-битной ОС:
git config --global core.editor \ "'C:/Program Files (x86)/Notepad++/notepad++.exe' -multiInst -notabbar -nosession -noPlugin"

Применение Task.WhenAll

Подскажите пожалуйста, в чем особенность использования Task.WhenAll ?
Полазив в msdn понял, что он создает новую задачу, по завершении указанных задач в параметре, однако, по сути он ничего не создает, кроме некой ссылки типа Task, с которой я не могу понять что делать дальше. Если здесь создается просто ссылка, которой я должен присвоить в дальнейшем новый объект типа Task, то тогда проще вызвать (имхо)
Task.WaitAll(t, t1); Task t2 = new Task(...);
или может быть я в неправильном направлении думаю. Подскажите пожалуйста пример использования этого метода. Спасибо.


Ответ

Сначала я неправильно прочитал вопрос и ответил про WaitAll
Допустим есть несколько экземплров Task, каждый из которых выполняет действие и возвращает результат. Затем, все эти результаты надо как-то обработать. WhenAll создаёт таск, который заканчивается, когда заканчиваются все таски в него переданные. При этом результатом этого таска будет массив результатов тасков аргументов.
Также можно проанализировать состояние переданных тасков, их исключения и так далее.
static void Main(string[] args) { var t1 = new Task(DoSomething1); var t2 = new Task(DoSomething2);
t1.Start(); t2.Start();
var t3 = Task.WhenAll(t1,t2);
Console.WriteLine(t3.Result.Sum()); }
private static int DoSomething2() { return 3; }
private static int DoSomething1() { return 5; }

Настройка smtp Yandex в Rails

Добрый день
Для реализации отправки сообщений из приложения было опробовано два пути: первый - компактный, и хоть не без проблем, но отправка сообщения происходит.
Второй (следуя руководству по ActionMailer) - не такой компактный и сообщения не отправляются.
Хочу разобраться с обоими
Вариант 1
#app/controllers/feedback_info_controller.rb
def send_mail smtp = Net::SMTP.new( "smtp.yandex.ru", 587 ) smtp.enable_starttls smtp.start( "yandex.ru", "my_adr@yandex.ru", "пароль", :plain ) do |conn| conn.send_message "Сообщение", "my_adr@yandex.ru", "получатель@rambler.ru" end
В этом случае, на почту получатель@rambler.ru приходит письмо (без темы) от MAILER-DAEMON@ со следующим содержанием:
by mail172.rambler.ru (rmaild SMTP 1.2.41) with ESMTP id 292593804 for получатель@rambler.ru; Mon, 24 Aug 2015 11:13:46 +0300 Received: from forward22m.cmail.yandex.net (forward22m.cmail.yandex.net [5.255.216.16])
by mx2.mail.rambler.ru (Postfix) with ESMTP id 65FF15CA8 for <получатель@rambler.ru>; Mon, 24 Aug 2015 11:13:46 +0300 (MSK) Received: from smtp3m.mail.yandex.net (smtp3m.mail.yandex.net [IPv6:2a02:6b8:0:2519::125])
by forward22m.cmail.yandex.net (Yandex) with ESMTP id 488F18046B for <получатель@rambler.ru>; Mon, 24 Aug 2015 11:13:46 +0300 (MSK) Received: from smtp3m.mail.yandex.net (localhost [127.0.0.1])
by smtp3m.mail.yandex.net (Yandex) with ESMTP id 2C1A127A05B8 for <получатель@rambler.ru>; Mon, 24 Aug 2015 11:13:46 +0300 (MSK) Received: by smtp3m.mail.yandex.net (nwsmtp/Yandex) with ESMTPSA id lnLUHk87ag-DjpWkB95; Mon, 24 Aug 2015 11:13:45 +0300 (using TLSv1 with cipher AES128-SHA (128/128 bits)) (Client certificate not present) Message-Id: <20150824111345.DjpWkB95@smtp3m.mail.yandex.net> Date: Mon, 24 Aug 2015 11:13:45 +0300 From: MAILER-DAEMON To: undisclosed-recipients:; X-Spam: yes
Вариант 2
Создание и использование рассыльщика FeedbackMailer
#app/mailers/application_mailer.rb class ApplicationMailer < ActionMailer::Base end
#app/mailers/feedback_mailer.rb class FeedbackMailer < ApplicationMailer def feedback_email mail(from: 'my_adr@yandex.ru', to: 'получатель@rambler.ru', subject: 'тема') end end
#app/controllers/feedback_info_controller.rb def feedback_send FeedbackMailer.feedback_email end
#config/environments/development.rb config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { address: 'smtp.yandex.ru', port: 587, domain: 'yandex.ru', authentication: 'plain', user_name: 'my_adr@yandex.ru', password: 'пароль', enable_starttls_auto: true }
с такими настройками ничего не происходит


Ответ

config.action_mailer.smtp_settings = { tls: true,
...
Без этого yandex не работает. Причем tls: в официальной документации нет.

Как скрыть див один за другим через javascript?

Как скрыть див один за другим через javascript? Я сделал но чета не получается!
$(document).ready(function() { setInterval(function(){ $('.div').hide(); next(); }, 1000); }); .div { width: 300px; margin: 10px; padding: 10px; background: #567; color: #fff; }

Div 1
Div 2
Div 3
Div 4
Div 5


Ответ

Можно сразу задать каждому элементу свой таймаут:
function delayedHide(i,o) { setTimeout( function(){ $(o).hide(); }, 600 * (i + 1) ); } $(function() { $('.hideme').each( delayedHide); }); .hideme { width: 300px; margin: 10px; padding: 10px; background: #567; color: #fff; }

Div 1
Div 2
Div 3
Div 4
Div 5

Не удержался, переименовал класс..

Пример группировки данных в LINQ

Нужно в массиве данных получить суммарное значение по полю разделенных по группам.
Пример:
IEnumerable Member Issue.Member decimal Issue.hours
Нужно пройти по массиву Issue, сгруппировать данные по member и для каждой группы сосчитать суммарное значение hours
Пытался следующим образом:
issues .Where(w => w.Member != null) .Group(g => g.Member) .Select(s => { s.Key // Здесь могу получить только ключ, а как получить данные и дальше их начать суммировать? });
По окончанию выборка должна содержать пару значений .


Ответ

Группировка поддерживает итерирование по ней, поэтому можно просто вызвать метод Sum()
var data = issues .Where(m => m != null) .Group(g => g.Member) .Select(s => new { s.Key, Sum = s.Sum(i => i.hours) });
// или .Select(s => Tuple.Create(s.Key, s.Sum(i => i.hours)));
data будет содержать список объектов с ключом группировки и значением суммы для каждой группировки.

Регулярное выражение различающее из какого набора строка

Есть два набора строк. Белый список:
bhdgffhfa aggjjaaga hbefebhfi dbceeabih ihifdbhhf djacfjbae ibjbeigac jaffigdad aaaaaaaaj dgifecchi dcbhgjaaf eedfbcedb aaaaaaabf beaajbihg aaaaaaaag eacccdjeh aaaaaaaad hehcijfhc aaaaaaaaa aaaaaaabc jfdhdeice
И черный список:
aaaaaaaai ehigcbgjd hgjdeajec aaaaaaabe haebgiggc aaaaaaaba aaaaaaaaf bfjhehbcf diieabefh jhjcaeead ifbjdgibf dddbgcgai bbcafhcif hfjciccbi fdbgidjdj aaaaaaabb jhdibgbfj cfjbaijad bhgjfacgi ehhieihhh abijjabda
Нужно написать регулярное выражение, не длинее 60 символов, способное различать из какого набора взята строка. Например, если на входе программе дано aaaaaaaaa, программа должна решить, что это строка из белого списка.
UPDATE: https://www.hackerrank.com/contests/regular-expresso/challenges/match-maker


Ответ

Возможно получение двух решающих паттернов: по попаданию в белый список (whitelist) и в чёрный список (blacklist).
Паттерн белого списка: /ce|ag|fe|ff|aaj|cd|dc|aad|abc|abf|acf|bei|bhh|cij|aaaaaaaaa/ Паттерн чёрного списка: /bg|ai|bb|ie|jc|jd|bd|cg|jh|aba|abe|aaaaaaaaf/
Паттерн чёрного списка короче и допускает сжатие до 37 символов: /ai|b[bdg]|cg|ie|j[cdh]|ab[ae]|a{8}f/ Но использование двух паттернов позволяет провести диагностику.
Решающие паттерны набираются из уникальных для соответствующего списка токенов по оптимизированному "жадному" алгоритму.
К рассмотрению приняты двух- и трёхсимвольные токены, а также 9-символьные токены, начинающиеся с "aaaaaaa".
Программа на языке PHP:
$whitelist = " bhdgffhfa aggjjaaga hbefebhfi dbceeabih ihifdbhhf djacfjbae ibjbeigac jaffigdad aaaaaaaaj dgifecchi dcbhgjaaf eedfbcedb aaaaaaabf beaajbihg aaaaaaaag eacccdjeh aaaaaaaad hehcijfhc aaaaaaaaa aaaaaaabc jfdhdeice ";
$blacklist = " aaaaaaaai ehigcbgjd hgjdeajec aaaaaaabe haebgiggc aaaaaaaba aaaaaaaaf bfjhehbcf diieabefh jhjcaeead ifbjdgibf dddbgcgai bbcafhcif hfjciccbi fdbgidjdj aaaaaaabb jhdibgbfj cfjbaijad bhgjfacgi ehhieihhh abijjabda ";
//Генератор решающих токенов function tokens_gen($whitelist, $blacklist){ $white_tokens = []; $black_tokens = []; for($i=-1; $i< 12; $i++){ $c = ($i<0) ? "" : chr(ord("a")+$i); // сначала двухсимвольные токены $c = ($i==10) ? "aaaaaaa" : $c; // последними - токены c "aaaaaaa" for($ord2=ord("a"); $ord2 <= ord("j"); $ord2++){ for($ord3=ord("a"); $ord3 <= ord("j"); $ord3++){ $ccc = $c.chr($ord2).chr($ord3); $pm_white = preg_match("/$ccc/", $whitelist); $pm_black = preg_match("/$ccc/", $blacklist); if(($pm_white==1) && ($pm_black==0)) array_push($white_tokens, $ccc); if(($pm_black==1) && ($pm_white==0)) array_push($black_tokens, $ccc); } } } return [$white_tokens, $black_tokens]; }
// "Жадный" рекурсивный отбор токенов function greedy($tokens, $list, $pat=""){ $tokens_new = []; $ppp_max = ""; foreach($tokens as $ppp){ $p = ($pat=="")? $ppp: "$pat|$ppp"; $sum = 0; foreach($list as $str) $sum += preg_match("/$p/", $str); if($sum > $sum_max){ $ppp_max = $ppp; $sum_max = $sum; } if($sum > 0) array_push($tokens_new, $ppp); } if($ppp_max == "") return $pat; $pat = ($pat=="")? $ppp_max: "$pat|$ppp_max"; $left = 'display: block; float: left; '; printf("Токенов:%d", count($tokens)); printf(" Строк, всего:%d по токену: $sum_max ", count($list)); print("Токен: $ppp_max   Паттерн: $pat
"); $list = preg_grep("/$ppp_max/", $list, PREG_GREP_INVERT); $pat = greedy($tokens_new, $list, $pat); return $pat; }
// Перевод контрольных строк в массивы $white = explode(chr(10), trim($whitelist)); foreach($white as &$w) $w=trim($w); $black = explode(chr(10), trim($blacklist)); foreach($black as &$b) $b=trim($b); $white_black = array_merge($white, $black);
// Получение решающих токенов list($white_tokens, $black_tokens) = tokens_gen($whitelist, $blacklist);
// Получение жадных паттернов print("Жадные паттерны:

"); $white_pattern = greedy($white_tokens, $white); $black_pattern = greedy($black_tokens, $black); //$black_pattern = 'ai|b[bdg]|cg|ie|j[cdh]|ab[ae]|a{8}f'; print("

white_pattern = $white_pattern
black_pattern = $black_pattern
");
$left = 'display: block; float: left; '; print("
Проверка:
"); foreach($white_black as $str){ $in_white = preg_match("/$white_pattern/",$str); $in_black = preg_match("/$black_pattern/",$str); print("
$str:$in_white $in_black"); }
Результаты:
Жадные паттерны:
Токенов:108 Строк, всего:21 по токену: 3 Токен: ce   Паттерн: ce Токенов:108 Строк, всего:18 по токену: 2 Токен: ag   Паттерн: ce|ag Токенов:88 Строк, всего:16 по токену: 2 Токен: fe   Паттерн: ce|ag|fe Токенов:81 Строк, всего:14 по токену: 2 Токен: ff   Паттерн: ce|ag|fe|ff Токенов:67 Строк, всего:12 по токену: 2 Токен: aaj   Паттерн: ce|ag|fe|ff|aaj Токенов:49 Строк, всего:10 по токену: 1 Токен: cd   Паттерн: ce|ag|fe|ff|aaj|cd Токенов:41 Строк, всего:9 по токену: 1 Токен: dc   Паттерн: ce|ag|fe|ff|aaj|cd|dc Токенов:33 Строк, всего:8 по токену: 1 Токен: aad   Паттерн: ce|ag|fe|ff|aaj|cd|dc|aad Токенов:28 Строк, всего:7 по токену: 1 Токен: abc   Паттерн: ce|ag|fe|ff|aaj|cd|dc|aad|abc Токенов:26 Строк, всего:6 по токену: 1 Токен: abf   Паттерн: ce|ag|fe|ff|aaj|cd|dc|aad|abc|abf Токенов:24 Строк, всего:5 по токену: 1 Токен: acf   Паттерн: ce|ag|fe|ff|aaj|cd|dc|aad|abc|abf|acf Токенов:22 Строк, всего:4 по токену: 1 Токен: bei   Паттерн: ce|ag|fe|ff|aaj|cd|dc|aad|abc|abf|acf|bei Токенов:18 Строк, всего:3 по токену: 1 Токен: bhh   Паттерн: ce|ag|fe|ff|aaj|cd|dc|aad|abc|abf|acf|bei|bhh Токенов:11 Строк, всего:2 по токену: 1 Токен: cij   Паттерн: ce|ag|fe|ff|aaj|cd|dc|aad|abc|abf|acf|bei|bhh|cij Токенов:5 Строк, всего:1 по токену: 1 Токен: aaaaaaaaa   Паттерн: ce|ag|fe|ff|aaj|cd|dc|aad|abc|abf|acf|bei|bhh|cij|aaaaaaaaa Токенов:118 Строк, всего:21 по токену: 5 Токен: bg   Паттерн: bg Токенов:118 Строк, всего:16 по токену: 2 Токен: ai   Паттерн: bg|ai Токенов:82 Строк, всего:14 по токену: 2 Токен: bb   Паттерн: bg|ai|bb Токенов:75 Строк, всего:12 по токену: 2 Токен: ie   Паттерн: bg|ai|bb|ie Токенов:67 Строк, всего:10 по токену: 2 Токен: jc   Паттерн: bg|ai|bb|ie|jc Токенов:53 Строк, всего:8 по токену: 2 Токен: jd   Паттерн: bg|ai|bb|ie|jc|jd Токенов:37 Строк, всего:6 по токену: 1 Токен: bd   Паттерн: bg|ai|bb|ie|jc|jd|bd Токенов:24 Строк, всего:5 по токену: 1 Токен: cg   Паттерн: bg|ai|bb|ie|jc|jd|bd|cg Токенов:18 Строк, всего:4 по токену: 1 Токен: jh   Паттерн: bg|ai|bb|ie|jc|jd|bd|cg|jh Токенов:12 Строк, всего:3 по токену: 1 Токен: aba   Паттерн: bg|ai|bb|ie|jc|jd|bd|cg|jh|aba Токенов:5 Строк, всего:2 по токену: 1 Токен: abe   Паттерн: bg|ai|bb|ie|jc|jd|bd|cg|jh|aba|abe Токенов:3 Строк, всего:1 по токену: 1 Токен: aaaaaaaaf   Паттерн: bg|ai|bb|ie|jc|jd|bd|cg|jh|aba|abe|aaaaaaaaf
white_pattern = ce|ag|fe|ff|aaj|cd|dc|aad|abc|abf|acf|bei|bhh|cij|aaaaaaaaa black_pattern = bg|ai|bb|ie|jc|jd|bd|cg|jh|aba|abe|aaaaaaaaf
Проверка:
bhdgffhfa:1 0 aggjjaaga:1 0 hbefebhfi:1 0 dbceeabih:1 0 ihifdbhhf:1 0 djacfjbae:1 0 ibjbeigac:1 0 jaffigdad:1 0 aaaaaaaaj:1 0 dgifecchi:1 0 dcbhgjaaf:1 0 eedfbcedb:1 0 aaaaaaabf:1 0 beaajbihg:1 0 aaaaaaaag:1 0 eacccdjeh:1 0 aaaaaaaad:1 0 hehcijfhc:1 0 aaaaaaaaa:1 0 aaaaaaabc:1 0 jfdhdeice:1 0 aaaaaaaai:0 1 ehigcbgjd:0 1 hgjdeajec:0 1 aaaaaaabe:0 1 haebgiggc:0 1 aaaaaaaba:0 1 aaaaaaaaf:0 1 bfjhehbcf:0 1 diieabefh:0 1 jhjcaeead:0 1 ifbjdgibf:0 1 dddbgcgai:0 1 bbcafhcif:0 1 hfjciccbi:0 1 fdbgidjdj:0 1 aaaaaaabb:0 1 jhdibgbfj:0 1 cfjbaijad:0 1 bhgjfacgi:0 1 ehhieihhh:0 1 abijjabda:0 1

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

Размытый фон div

Как реализовать blur эффект фона div элемента, чтобы он подстраивался под background body.
Вот пример


Ответ

Есть не суперкроссбраузерная, но рабочая штука, css-свойство filter
div { background: url(http://lorempixel.com/800/800/people/3/); width: 800px; height: 800px; position: relative; } div> div { position: absolute; width: 200px; height: 200px; overflow: hidden; background: transparent; background: url(http://lorempixel.com/800/800/people/3/) -300px 500px; left: 300px; top: 300px; -webkit-filter: blur(5px); -moz-filter: blur(5px); -o-filter: blur(5px); -ms-filter: blur(5px); filter: blur(5px); }


Подробнее здесь - https://webref.ru/css/filter Ну и спецификации - http://www.w3schools.com/cssref/css3_pr_filter.asp

Redis, как кэш для SQL запросов в веб-приложении

Я впервые использую редис в веб-приложении. Изначально, цель была - кэшировать результаты сложных запросов, которых было не очень много, это не так сложно, и я подумал, почему бы не кэшировать всё? Ну или почти всё.
И так, я написал конструктор mysql-запросов, который всегда перед тем как выполнить запрос к бд, принимает необязательный параметр - ключ, по которому хранятся данные в redis. В итоге если такой ключ был передан, конструктор сначала посмотрит, есть ли данные по ключу в редисе, и если есть - то он вернет данные из редиса и к мускулу обращаться не будет, а если нет - то выполнит sql-запрос, вернет данные в контроллер и в соседнем потоке запишет их в редис (используя тот самый, переданный ключ). С запросами на выборку понятно, а вот если мы делаем insert/update, то мы удаляем из редиса все данные которые попали под маску переданного ключа, и тогда при новой выборке кэш обновится.
Все что попадает в редис, в основном хранится в виде JSON. И, вот настал тот ужасный момент, когда количество хранимых данных стало большим (а на проде, будет еще в over 9999 раз больше), и самое важное - много дублирующихся данных. Приведу простой пример. Мы заходим в раздел articles, загружаются посты которые находятся на первой странице (пусть их по умолчанию отображается по 50). В этот момент, контроллер сформировал ключ articles:list:page_1 по которому будет искать данные в редисе. Дальше походили по страницам, сформировали кэш для articles:list:page_2 articles:list:page_5 articles:list:page_19 articles:list:page_28 .... После чего, нажали на кнопку "Показать все статьи", тем обратились по ключу articles:list:all - понятно, что тут хранятся все записи (и я думаю - это ужасно!). Потом мы открыли несколько статей articles:post:id1234 articles:post:id567 articles:post:id890. Вы представьте сколько уже есть дублирующих данных в редисе, а если мы дадим пользователю выбирать кол-во выводимых записей, то тогда появятся такие ключи: articles:list:page_1_55 articles:list:page_3_55 и тд (_55 записей на странице). Это приведет к большой беде. Теперь дальше, если в админке мы отредактируем какой-то пост, а заголовок этого поста выводится в листинге, тогда мы удалим из кэша старые данные так articles:post:id890 и очистим весь листинг articles:list:* - что есть тоже очень плохо.
А что касается сложных запросов к бд, есть еще один случай, когда сам пост хранится в одной таблице, комменты к нему в другой, лайки в третьей и еще 5 таких зависимостей. Сейчас (без редиса) это вытягивается с помощью сложного запроса с JOIN'ами (допустим их 7 шт.). Это все можно сохранить в редис, но тогда если кто-то лайкнет пост, нужно инвалидировать кэш, и заново придется выполнять сложный запрос. Есть вариант разбить это на 7 простых запросов и сохранить каждый в редисе, и тогда если кто-то оставит коммент или лайкнет, то нужно будет только один простой запрос выполнить. Вот, как в такой ситуации было бы правильно поступить? (не будем говорить о тестах, будем рассуждать примерно, берем какие-то средние значения и среднестатичные случаи).
Что в этом случае вы посоветуете делать? Как правильно реализовать хранение данных в кэше? Возможно стоит пересмотреть способ хранения, может не хранить коллекции :list: целиком, а извлекать всегда какие-то части (элементы)? Я вот не могу в этом разобраться.
p.s. буду благодарен за ссылки на интересные статьи по теме (желат. на русском), и по настройке редис на сервере, да и вообще как можно больше про редис хочется узнать (не просто из постов на хабре, а что-то более подробное и доходчивое для человека который впервые с ним работает)


Ответ

Вы столкнулись с некоторыми вещами, которые не очень хорошо помещаются в тот мир, в котором вы работали раньше. В какой-то степени можно это назвать просто NoSQL, но это не совсем так, в любом случае могу поздравить с важным шагом в карьере.
Дублирование данных
Первое, о чем хотелось бы сказать:
самое важное - много дублирующихся данных
Дублирующиеся данные сами по себе нормальны. Нормализация базы данных SQL невольно учит нас тому, чтобы данные были в единичном экземпляре, но это, на самом деле, не аксиома. Дублирующиеся данные сложнее поддерживать, но само их наличие в целях улучшения работы приложения - это абсолютно нормально. Кроме этого, стоит вставить мою любимую ремарку про модель работы приложения: если сейчас у вас это де-факто pull-on-change - сформировать данные по запросу, то есть гораздо более интересная модель push-on-change - когда данные для запросов подготавливаются при добавлении, и в этом дублирование данных подразумевается само по себе. Представьте себе некую социальную сеть с лентой новостей. При стандартном подходе (и SQL-бэкенде) придется формировать гигантский SQL-запрос, который будет проверять необходимость показа каждой записи, наличие доступа к ней (мы же не хотим показывать приватные записи, верно?) и прочие атрибуты. Push-on-change же предлагает в этом случае в момент обновления записи в ленте вычислять, кому она должна быть показана, и формировать таблицу вида 'id пользователя | id записи | datetime', чтобы просто доставать из нее N первых записей, по которым уже формировать запрос по новостям. Данные в этом случае де-факто будут продублированы, но это необходимо, чтобы ускорить многократные read-операции за счет однократной write-операции. С переходом в хайлоад это становится особенно актуально, потому что один сервер не в состоянии хранить все данные и/или выдержать нагрузку, а поэтому необходимо разделять ответственность между серверами (что может поставить запрет на джойны, например). Однако,
Отказ от подготовки ответов на все запросы
геометрическая прогрессия, конечно, не позволяет подготовить заранее готовые ответы на все возможные запросы:
Вы представьте сколько уже есть дублирующих данных в редисе, а если мы дадим пользователю выбирать кол-во выводимых записей, то тогда появятся такие ключи: articles:list:page_1_55
поэтому от вышеприведенного примера действительно стоит отказаться - у Redis нет столько оперативки (как и от :all - тут проблема не в оперативке, а в количестве данных, которые надо будет передать по сети и затем разобрать). Конкретно в вашем случае есть две вещи, которые я считаю необходимым отметить
Текущий подход работает, де-факто, на кэширование конкретных запросов Redis используется как KeyValue-кэш
Первое подразумевает тот самый рост данных в геометрической прогрессии со степенью, зависящей от количества параметров, по которым может проводиться выборка. Вам не обязательно получать из Redis уже готовый ответ - вы можете получать его либо по частям, либо бОльшим куском. В случае со страницами по 55 записей вы можете просто хранить "верхушку" таблицы в редисе:
# пусть верхушка состоит из 500 записей from = 63 to = 63 + 55 if to < 500: top_entries = redis.get 'articles:top' # в редисе может не оказаться ничего или оказаться устаревший и слишком короткий список if top_entries && top_entries.length > to: return top_entries.slice from, to return article_repository.get_slice from, to
В этом случае приложение знает, что у него (возможно) есть кусман на 500 записей в редисе, и запрос, скорее всего, проще обслужить оттуда, поэтому пытается сделать именно это.
В случае со сложной иерархической структурой проще наоборот, разбить сущность на составляющие:
article = redis.get 'article:' + id if not article: article = article_repository.get id if not article: throw new ResourceNotFoundException comments = redis.get 'comments:by-article:' + id if not comments: comments = comment_repository.get_by_article id likes = redis.get '
В этом случае вы делаете из сущностей некоторое подобие строительного материала, который не слишком сильно дублируется и позволяет собирать различные результаты на основе одних и тех же источников (например, поиск по статьям в любом случае пойдет через БД, но лайки для них можно вытащить из Redis без особых затрат по времени). Это не самый богатый арсенал, но он поможет избежать избыточного дублирования, которое может возникнуть, если иерархические сущности кэшируются целиком, и, заодно, уменьшит развесистость операции, необходимой для обновления одной атомарной сущности (т.е. не придется сбрасывать половину кэша из-за одного проставленного лайка).
Закешировать всё
Кроме всего вышеописанного, я бы относился немного более практично к идее закешировать всё. Сейчас вы пытаетесь уменьшить нагрузку на сервис за счет кэширования, но переносите в Redis буквально вообще все, но это вам не нужно. До десятой страницы новостей доберется один читатель из сотни - вам действительно нужно оптимизировать ее быстродействие? В том случае, если она срендерится за 100 мс, а не за 10, пользователь это не почувствует, и сервер тоже не почувствует, потому что основная нагрузка у него идет на получение первых записей.
LazyLoad
Все вышеописанное только вскользь говорило о том, как данные попадают в Redis - что в случае добавления записи необходимо пересчитать специально подготовленную выборку. Однако мы знаем, что записи в любом in-memory кэш-сервисе (будь то Redis, Memcache, Aerospike) вечны максимум вплоть до первой перезагрузки машины, и даже подготовленные выборки в этом случае умрут. В этом случае поможет механизм ленивой загрузки - если грубо, то он пытается получить данные из некоего источника данных А, и, если не находит, загружает их из источника Б, кладет в А, и возвращает. В программировании этот подход часто применяется для инициализации тяжелых объектов и вызовов из БД, которые могут быть не нужны:
private heavy_object private settings
get_heavy_object(): if not heavy_object: heavy_object = new HeavyObject return heavy_object
get_settings(): if not settings: settings = read_settings_from_database() return settings
В случае с кэшем хранилищем А является не переменная, а кэш, а хранилищем Б - БД:
get_article(id): redis_id = 'article:' + id article = redis.get redis_id if not article: # не обнаружен в Redis - ищем в БД article = repository.get id if not article: # значит, он вообще не существует return null redis.put id, article # кладем в Redis для последующих вызовов return article
Если вы будете применять эту парадигму везде, то приложению можно будет хоть живьем подменить Redis, оно все равно будет работать. Кроме того - для меня это самый важный пункт - в этом случае можно спокойно инвалидировать существующий кэш, не боясь за само приложение (но, конечно, стоит иметь в виду резко возрастающую нагрузку на БД).
Инвалидация (и немного про CAP)
Следующий вопрос, который у вас так же возникает - это инвалидация данных, т.е., когда они должны исчезнуть из кэша или обновиться. Самый напроломный вариант - это обновление данных in-place, т.е., как только обновилась сущность, обновились и все связи в кэше. Однако, это плохой вариант - точнее, он отличный, но его невероятно сложно реализовать, не забыв про что-то и не раздув код до невероятных размеров. В случае, если кэш условно-бесконечен, и какой-то тип записей не обновляется, то все пользователи будут видеть устаревшие данных и спрашивать у вас, почему на разных страницах у одной и той же статьи разные комментарии. Тут на помощь приходят две стратегии инвалидации, которые есть в Redis:
LRU (Least Recently Used). При переполнении указанного в параметре max-memory размера Redis начнет удалять записи, которые не запрашивались дольше всех. Это гарантирует некоторую ротацию записей (при добавлении новых записей некоторые из старых будут удаляться, чтобы, при повторном запросе, быть обновленными из БД) TTL (Time To Live). Запись с указанным TTL может быть удалена по истечении этого TTL (может быть - в связи с проблемами в выполнении задачи не могу сказать, насколько вовремя это сделает Redis, но вряд ли задержка составит больше трех секунд). Именно это я и хочу предложить в качестве серебряной пули - делайте всем записям TTL в пределах 1-60 секунд, и устаревшие данные у вас гарантированно не продержатся дольше минуты, а благодаря lazy load воскреснут вновь.
Резюмируя, проще всего задавать небольшое время жизни записям, и сильно не беспокоиться об инвалидации, пока продукт не вышел на поддержку. В этом случае у вас страдает целостность данных (consistency), но в большинстве случаев она на самом деле не является критичной, более того, существует применимая к распределенным системам теорема CAP (NB: обсуждаемое в этом ответе приложение - это не распределенная система), которая (если очень грубо) говорит о том, что невозможно одновременно поддерживать доступность и согласованность данных - это просто некоторое свойство нашей вселенной, и небольшая задержка в обновлении данных чаще всего не только остается незамеченной, но и не всегда может быть замеченной (если у вас есть задержка в обновлении выдачи статей, то пользователь не ожидает получить новую в тот же момент, когда она была написана - он не знает момента написания).
Dogpile effect
В связи с параграфом про TTL и lazyload нельзя пропустить так называемый эффект собачьей стаи. С момента запроса обнаружения пустоты в кэше первым клиентом до момента его обновления проходит некоторое время X, за которое могут прийти еще N клиентов. Приложение в этом случае добросовестно попытается реконструировать кэш еще N раз. Единого решения у этой проблемы нет, но вы можете либо ставить лок на время обновления кэша (но если у вас приложение стоит более, чем на одном сервере, то придется использовать сервис, поддерживающий распределенные блокировки - например, consul, etcd или даже реализация с помощью самого Redis - см. Redlock), либо просто считать, что в SLA приложения добавлена возможность работать без кэша вообще (если за кэшем у вас скрывается один запрос для одной записи в БД, то почему бы и нет).
Поиск
И, наконец, самое веселое. Как объединить сам поиск и подготовку данных для него? В общем случае - никак, ищите по базе, вы не сможете запихнуть все необходимое для запроса в KV-хранилище. Но если вас интересует, как это решается в серьезных случаях (имеется в виду поиск по базе данных, а не уровня яндекса/гугла, конечно), то есть специализированные решения, например, Lucene (и построенный на его основе ElasticSearch), Solr, Sphinx, YoctoDB. В общем случае поиск сводится к построению индексов из полей документа, поиску по этим индексам и агрегированию результата.
Микросервисная архитектура
Предложенные мной решения по разбиению на building blocks так или иначе приведут к появлению в приложении менеджеров каждой сущности, каждый из которых заведует отдельной сущностью. Хочется сказать, что это разбиение может продолжиться и вне приложения - приложение можно разбить на отдельные приложения, каждое из которых будет заниматься своим доменом. Это так же убьет всякую возможность джойнов, но, на самом деле, она и не нужна. Если пользователи у вас лежат в одном приложении на одном кластере, а статьи, в которых автор указан в виде идентификатор - в другом приложении на другом кластере, то вряд ли вам потребуется искать все статьи, в имени автора которого стоит "Андрей".
Как обо всем позаботиться?
Скорее всего, через весь ответ так и сквозят вопросы вроде "какой TTL ставить", "нужно ли кэшировать Х" и "какйо прирост производительности я получу". Ответ простой - это вообще никак не узнать, это выясняется только на практике. Деплойте приложение, анализируйте статистику, и через некоторое время вы просто поймете, где оно подтормаживает. Если оно не подтормаживает в месте, где вы забыли поставить кэш - возможно, там и нет смертельной необходимости в нем, и к этому месту нужно будет вернуться, если вообще не останется других задач?
Просто пара слов про Redis
Во-первых просто хотелось сказать, что кроме Redis есть еще сервисы, выполняющие те же функции (мой любимый - Aerospike). Redis довольно прост в использовании, но не умеет, например, шардиться, у него были проблемы с фрагментацией памяти (порог в 100 мб теоретически имел право забрать до 200 мб оперативки), он однопоточный и в мире NoSQL довольно похож на MySQL в мире баз данных. Тем не менее, с небольшим приложением по меркам интернет-гигантов он наверняка справится без особых проблем.

Java - обращение к полю подкласса, если его экземпляр присвоен ссылке на супер-класс и поля имеют одинаковые имена и дефолтный модификатор доступа

Изучая наследование и полиморфизм в java наткнулся на такой пример:
class A { int a = 5; String doA() { return“ a1“; } protected static String doA2() { return“ a2“; } } class B extends A { int a = 7; String doA() { return“ b1“; } public static String doA2() { return“ b2“; } void go() { A myA = new B(); System.out.print(myA.doA() + myA.doA2() + myA.a); } public static void main(String[] args) { new B().go(); } }
Результат выполнения программы: "b1 a2 5"
b1 - тут понятно. фактический тип класса является B, через динамическое связывание компилятор вызывает метод doA(), определенный в классе B. a2 - метод doA2() определен статическим, соответственно происходит ранее связывание, компилятор вызывает метод класса A, а не экземпляра 5 - вот тут-то для меня и происходит магия. почему не 7? переменная не static и не final? фактический тип класса B


Ответ

A myA = new B(); ^ Потому что поля в Java не являются полиморфными, соответственно используется класс указателя, в данном случае это класс A
По поводу методов: в Java все методы являются виртуальными, соответственно используется динамическое (или, как его еще называют, позднее) связывание. Поэтому на этапе выполнения JVM определяет тип объекта B, на который ссылается указатель myA и вызывает соответствующую реализацию метода doA() класса B

Как удобно создавать Immutable Value Object с большим количеством свойств в C#

Задача в том, чтобы с наименьшим количеством кода определить немутируемый (immutable) ValueObject с большим количеством свойств.
Использую NHibernate как ORM, поэтому свойства должны быть virtual и public/protected. Этот объект мэпится к таблице из БД.
Этот объект не должен меняться, но надо, чтобы его можно было создать.
Если свойств не много, то проблем нет, например так:
public class ElectricDevice { public virtual int Id { get; protected set; } public virtual string Name { get; protected set; } public virtual bool IsDecommissioned { get; protected set;}
public ElectricDevice(int id, string name, bool isDecommissioned) { Id = id; Name = name; IsDecommissioned = isDecommissioned; } }
Но если свойств становится много, например, 20, то как-то неудобно, долго писать класс (таких классов к тому же должно быть много). Так же не красиво создавать такой объект через конструктор. Есть какие-нибудь идеи? Может быть в C#6 какой-нибудь новый синтаксис?


Ответ

Если нужен fluent-интерфейс, у меня получилось вот такое решение, основанное на ответе @Stack:
Заводим атрибут, чтобы отмечать классы, к которым нужно строить Builder
namespace BuildCodegen { class CreateBuilderAttribute : Attribute { } }
Помечаем этим атрибутом наш класс:
[CreateBuilder] public class ElectricDevice { public virtual int Id { get; protected set; } public virtual string Name { get; protected set; } public virtual bool IsDecommissioned { get; protected set; }
public ElectricDevice(int id, string name, bool isDecommissioned) { Id = id; Name = name; IsDecommissioned = isDecommissioned; } } Заводим в нашем проекте TextTemplate (в соседнем ответе расписано, как именно это делается), называем его Builders.tt
Для доступа к существующему коду используем CodeModel, а не рефлексию, т. к. у рефлексии есть известные проблемы (несвоевременное обновление, блокировка сборок в памяти).
Итак, помещаем в Builders.tt следующий код:
<# /* hostspecific = true, т. к. мы используем CodeModel Visual Studio */ #> <#@ template debug="false" hostspecific="true" language="C#" #> <#@ assembly name="System.Core" #> <#@ assembly name="System.Data" #> <#@ assembly name="EnvDte" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.Text" #> <#@ import namespace="System.Collections" #> <#@ import namespace="System.Collections.Generic" #> <#@ import namespace="EnvDTE" #> <#@ output extension=".cs" #> <# // задаём вручную имя атрибута (как сделать лучше?) var attributeFullName = "BuildCodegen.CreateBuilderAttribute"; var visualStudio = (EnvDTE.DTE)((this.Host as IServiceProvider) .GetService(typeof(EnvDTE.DTE))); var project = (EnvDTE.Project)visualStudio.Solution .FindProjectItem(this.Host.TemplateFile).ContainingProject; var codeModel = project.CodeModel; // получили модель кода: var classes = codeModel.CodeElements.Cast().SelectMany(GetClasses); var classesThatNeedBuilder = classes.Where(c => c.Attributes.Cast() .Any(attr => attr.FullName == attributeFullName)); foreach (var ccl in classesThatNeedBuilder) { var namespaceName = ccl.Namespace.FullName; var className = ccl.Name; var builderName = className + "Builder"; var properties = ccl.Children .OfType() .Select(p => new PropertyDescriptor(p)) .ToList(); // Каждый builder кладём в то же пространство имён, что и // производимый им класс. (Это легко переделать, разумеется.) // Также мы предполагаем, что у производимого класса есть // конструктор, принимающий все атрибуты, с именами, соответствующими // именам свойств, но со строчной буквы (если это не так, нужно // поискать конструктор и имена параметров через CodeModel) #>
namespace <#= namespaceName #> { public class <#= builderName #> { public <#= className #> Build() { return new <#= className #>(<#= string.Join(", ", properties.Select(p => p.LowerName + ": " + p.LowerName)) #>); } <# foreach (var prop in properties) { // для каждого из свойств определяем несущее поле и // fluent-метод его установки #>
<#= prop.Type #> <#= prop.LowerName #>;
public <#= builderName #> With<#= prop.UpperName #>(<#= prop.Type #> <#= prop.LowerName #>) { this.<#= prop.LowerName #> = <#= prop.LowerName #>; return this; } <# } #> } }
<# } #>
<#+ // вспомогательный метод: получаем рекурсивно список классов // мы смотрим только внутри пространств имён, но не внутри других классов // (исключительно из-за лени, ну и нужно для вложенного класса придумать, // куда же класть builder) IEnumerable GetClasses(CodeElement elt) { CodeClass ccl = elt as CodeClass; if (ccl != null) return new[] { ccl }; CodeNamespace cns = elt as CodeNamespace; if (cns != null) return cns.Members.Cast().SelectMany(GetClasses); return Enumerable.Empty(); }
// ну и мелкий класс-обёртка для свойства class PropertyDescriptor { public readonly string Type; public readonly string UpperName; public readonly string LowerName;
public PropertyDescriptor(CodeProperty property) { this.Type = property.Type.AsString; var name = property.Name; this.UpperName = char.ToUpper(name[0]) + name.Substring(1); this.LowerName = char.ToLower(name[0]) + name.Substring(1); } } #> Получаем такой автоматически сгенерированный класс:
namespace BuildCodegen { public class ElectricDeviceBuilder { public ElectricDevice Build() { return new ElectricDevice(id: id, name: name, isDecommissioned: isDecommissioned); }
int id;
public ElectricDeviceBuilder WithId(int id) { this.id = id; return this; }
string name;
public ElectricDeviceBuilder WithName(string name) { this.name = name; return this; }
bool isDecommissioned;
public ElectricDeviceBuilder WithIsDecommissioned(bool isDecommissioned) { this.isDecommissioned = isDecommissioned; return this; } } }
Кстати, сам Entity-класс тоже можно генерировать автоматически.

Для чего приведение к типу в методе findViewById()

Изучаю ANDROID, вот пример листинга:
Button newMyBtn = (Button) findViewById(R.id.myBtn);
Синтаксически запись правильна, только я вот одного понять не могу, зачем здесь вот это самое приведения объектной ссылки? Что, нельзя в объект newMyBtn сразу положить вызов метода findViewById?
Я почитал Хорстаманна и нашел (но все равно остался непонятен принцип данного приведения):
Чтобы выполнить приведение объектной ссылки, используются такие же синтаксические конструкции, как и для числовых выражений. Имя требуемого класса надо поместить в скобки и поставить перед объектной ссылкой, которую нужно преобразовать. Пример соответствующего выражения приведен ниже.
Manager boss = (Manager) staff[0]; Для приведения типов существует только одна причина — необходимость использовать все возможности объекта после того, как его фактический тип был на время забыт. Например, в классе ManagerTest массив staff представляет собой массив объектов Employee. Этот тип выбран, поскольку некоторые элементы массива хранят информацию об обычных сотрудниках. Чтобы получить доступ ко всем новым полям класса Manager, нам может понадобиться привести тип некоторых элементов массива к типу Manager. (В примере, рассмотренном выше, мы предприняли специальные меры, чтобы избежать приведения типов. Мы инициализировали переменную boss объектом Manager, перед тем как поместить ее в массив. Для того чтобы задать размер премии, нужно знать правильный тип объекта.)


Ответ

Проблема в различии двух вещей: декларируемого и фактического типа объекта.
Дело в том, что метод findViewById не может знать, что по данному id будет лежать именно Button. Поэтому он декларирует тип возвращаемого объекта как View — это максимальная гарантия, которую он может дать. Фактически возвращённый объект может быть любого производного от View типа.
Итак, у нас findViewById возвращает View, который, как мы уверены, фактически будет Button'ом. Для того, чтобы работать с ним как с Button'ом, мы должны иметь ссылку типа Button. Мы её декларируем: Button newMyBtn
Теперь, у нас инициализация ссылки типа Button другой ссылкой, у которой декларируемый тип — всего лишь View. Система не уверена в том, что фактическим типом будет Button. Для того, чтобы убедить её в этом, мы используем приведение к нужному типу (Button)findViewById(...)
Эта конструкция фактически означает: «Я уверен, что фактический тип объекта будет Button или производный от него. Причём настолько уверен, что согласен крешнуть программу, если это не так.» (Точнее, выбросить исключение, да.) При этом компилятор соглашается с вашими доводами, и приводит декларируемый тип View к Button. (Если во время выполнения там реально будет не Button, вы таки получите исключение.)
Без приведения вы не убедите компилятор, что тип у ссылки правильный.
Почему для компилятора важно убедиться в правильности типа? Потому что если у вас ссылка newMyBtn имеет заявленный тип Button, то вы можете у неё вызывать методы, специфичные для Button'а, и обращаться к полям, которые есть в Button'е. Если этот объект окажется фактически другого типа, и таких полей в нём не будет, что тогда? Компилятор заботится о том, чтобы таких ситуаций не возникало. (Это называется словом «типобезопасность».)

Что такое лексикографическое сравнение и что оно собой представляет?

Встретил в книге термин лексикографическое сравнение хотелось бы понять что это такое.


Ответ

Это сравнение "как в словаре" или "как в телефонном справочнике" - по алфавиту. Если первые буквы совпали, сравниваются вторые, если вторые совпали - третьи, и так далее.

Как обрабатываются запросы к БД с несколькими условиями?

Подскажите пожалуйста: если в запросе несколько условий, например, WHERE a=1 AND b=2, то сначала выбираются данные, удовлетворяющие условию a=1, а потом среди них выбираются данные по условию b=2? Или для второго условия поиск идет опять по всей таблице? Целесообразен, конечно, первый вариант, но что-то нигде не нашел явного подтверждения.


Ответ

Зависит от индексов и изобретательности оптимизатора. Ну и надо понимать, что разработчики базы, как правило, достаточно опытные люди.
Проверить всё это можно, запустив EXPLAIN вашего запроса.
Если индексов на эти поля нет
...то надо принять решение: делать по две проверки в одном проходе или два прохода по одной проверке. Если данные целиком помещаются в кэше процессора (почти нереальный случай), то разница заметна не будет.
Если не помещаются, то в кэш будут последовательно загружаться разные участки проверяемого набора данных из оперативной памяти (если набор данных туда помещается). Загрузка в кэш занимает какое-то микровремя, но оно становится тем больше, чем больше таблица. Делать два прохода выходит уже дороже: на прежний объём сравнений нужно сделать вдвое больше загрузок в кэш.
Если данные не помещаются даже в оперативную память, всё совсем очевидно — загрузка с диска очень долгая. Настолько, что остальные части запроса вряд ли будут заметны, и выполнение двух обходов, скорее всего, примерно вдвое увеличит время выполнения запроса.
Итог: обход один, две проверки на каждый ряд выглядят рациональнее со всех сторон.
Если есть индекс на одном из полей
...то всё очевидно: сходу есть возможность "дёшево" (по ресурсам) сократить перебираемый набор данных до части таблицы с заданным значением в проиндексированном поле. А уже по этому набору выполнить последовательный перебор и проверить второе условие.
Если есть индексы на двух полях
...то  фиг  EXPLAIN его знает! Более достоверного ответа никто не даст, каждая БД решает этот вопрос как-то по-своему.
Оптимизатор может посмотреть в оба индекса, прикинуть где последовательный перебор окажется меньше и использовать его. А может ошибиться и взять не тот, руководствуясь какими-то своими соображениями.
PostgreSQL умеет делать bitmap scan, в ходе которого он сканирует оба индекса и составляет карту (bitmap) по каждому условию, а затем объединяет две карты в одну согласно условиям, получая результат. Но делать ли это, решает оптимизатор
В идеале: если есть индекс по парам значений
Здесь индекс просто используется напрямую, поэтому порядок поиска будет совпадать с порядком индексирования: сначала спуск с "верхних уровней индекса" (по первому выражению), а затем спуск по второму и сразу получает ответ
Но всё это неважно, если...
данных совсем мало небольшие объёмы данных может быть быстрее обойти без индекса оптимизатор облажается (что бывает) отладка чего превращается в охоту за индексами путём чтения EXPLAINов

Как проявляется contentDescription

В учебнике по Android'у несколько раз упоминается про параметр (и метод) contentDescription. К примеру, в разметке я его устанавливал в ImageView



Но я никак не могу понять для чего это! На картинку и нажимал, и удерживал, но никаких эффектов, текста и тд. Так для чего же этот contentDescription и как он проявляется?


Ответ

Это для слабовидящих. Текст в значении этого аттрибута будет озвучен системой при нажатии на картинку в случае, если в настройках устройства включена соответствующая опция.
Озвучка идёт чрез гугловое приложение

Разница между begin и cbegin

В чём разница между begin и cbegin, end и cend и т.д.?


Ответ

Методы с префиксом c возвращают константные итераторы. Это означает, что
Через них нельзя изменять значения. А ещё их можно использовать с константными объектами.

Отличие try…catch от throws

В чем принципиальное отличие
try {
}catch(Exception e){
}
от throws


Ответ

При обработке исключений всегда есть 2 варианта : либо пробрасываем выше (т.е. добавляем throws к сигнатуре метода), либо обрабатываем "на месте", используя try-catch

Какие основные задачи bare репозитория?

Насколько я понял, bare репозиторий создается как буфер между основной копией на сервере и ветками разработки.
Но не до конца понимаю, читая статью «удачная модель ветвления», зачем нужен этот тип репозитория, если все должно работать и так.


Ответ

bare-репозиторий — это не какой-то особый тип репозитория. это, собственно, и есть репозиторий

командой git init --bare в текущем каталоге создаётся именно репозиторий:
$ git init --bare $ ls -F branches/ config* description HEAD hooks/ info/ objects/ refs/

а вот командой git init (без опции --bare) репозиторий создаётся в каталоге .git
$ git init $ ls -F .git branches/ config* description HEAD hooks/ info/ objects/ refs/
что позволяет использовать текущий каталог как рабочий каталог (working dir) с извлечёнными из репозитория файлами/каталогами, версии которых и отслеживаются этим самым репозиторием (вообще рабочий каталог может находиться в совершенно ином месте и указываться программе git с помощью опций -C или --work-tree или их комбинации (эти опции не эквивалентны), или переменной окружения GIT_WORK_TREE, или конфигурационной переменной core.worktree).

«превратить» bare в не-bare (и обратно) очень легко: достаточно изменить значение (true или false) переменной bare в секции [core] файла config. вручную или командой git config core.bare значение. т.е., фактически, это некий признак, на практике означающий, что «рядом есть рабочий каталог с отслеживаемыми файлами».

зачем нужен этот признак?
чтобы при попытке «запушить» в текущую ветку (ту, на которую указывает содержимое файла HEAD) «репозитория с рабочим каталогом» (не-bare-репозиторий) пользователь получал ошибку и подробное объяснение:
remote: error: refusing to update checked out branch: refs/heads/master remote: error: By default, updating the current branch in a non-bare repository remote: error: is denied, because it will make the index and work tree inconsistent remote: error: with what you pushed, and will require 'git reset --hard' to match remote: error: the work tree to HEAD. remote: error: remote: error: You can set 'receive.denyCurrentBranch' configuration variable to remote: error: 'ignore' or 'warn' in the remote repository to allow pushing into remote: error: its current branch; however, this is not recommended unless yo remote: error: arranged to update its work tree to match what you pushed in some remote: error: other way. remote: error: remote: error: To squelch this message and still keep the default behaviour, set remote: error: 'receive.denyCurrentBranch' configuration variable to 'refuse'
мой вольный перевод:
отказано в обновлении ветки refs/heads/master, из которой извлечены файлы в рабочий каталог. по умолчанию обновление текущей ветки в не-bare-репозитории запрещено, потому что это приведёт к несоответствию между тем, что вы «пушите», и индексом и содержимым рабочего каталога, и потребует выполнения git reset --hard для приведения рабочего каталога в соответствие с текущей веткой, т.е. той, которая указана в файле HEAD. вы можете установить значение конфигурационной переменной receive.denyCurrentBranch удалённого репозитория в значение ignore или warn для разрешения «пушить» в его текущую ветку; тем не менее этого не рекомендуется делать, если только вы не настроили (автоматическое) обновление рабочего каталога каким-либо иным путём. вы можете отключить выдачу этого сообщения, не меняя умолчального поведения, установив переменной receive.denyCurrentBranch значение refuse

Необработанное исключение

На собеседовании спросили что будет в данной ситуации если оба Task выбросят исключение и как в данном случае их нужно обрабатывать?
Task task1 = Method1Async(); Task task2 = Method2Async(); await task1; await task2;


Ответ

Сперва немного теории. Как известно, async/await представляет по сути лишь синтаксический сахар, за кулисами же компилятор разворачивает асинхронный код в чуть более сложный. Что происходит, когда в методе, помеченном модификатором async, возникает исключение? Оно не выбрасывается тотчас наверх по стеку (потому что к тому моменту мы уже можем быть в совершенно другом месте), а помещается внутрь таска (см. свойство Exception). Сам метод при этом завершается нормально. Исключение же (оригинальное или обернутое в AggregateException) будет выброшено в нескольких случаях:
Кто-то ожидает таск (с помощью await или метода Wait()). Кто-то обращается к результату (свойство Result, применимо только для Task). В случае если таск остался unobserved (т.е. его никто не дождался и не обратился к результату), то когда сборщик мусора добирается до таска и вызывает его финализатор, то финализатор видит, что в таске есть исключение и выбрасывает его. Как следствие, процесс падает. Важное замечание: такое поведение было включено по умолчанию до .NET 4.5. Начиная с .NET 4.5, финализатор не выбрасывает исключение, однако прежнее поведение может быть возвращено с помощью настройки ThrowUnobservedTaskExceptions

Исходя из этого, ответ на вопрос несколько "ветвист":
Вариант 1. Код работает под .NET младше 4.5, либо включена настройка ThrowUnobservedTaskExceptions
В таком случае исключение для первого таска будет выброшено, исключение второго таска останется unobserved. Если где-то выше по стэку исключение от первого таска было обработано, и приложение продолжило работу, то рано оно поздно оно упадет -- когда финализатор второго таска выбросит исключение.
Вариант 2. Код работает под .NET 4.5 и выше и настройка ThrowUnobservedTaskExceptions выключена.
В таком случае исключение для первого таска по-прежнему будет выброшено, исключение второго таска останется unobserved (увидеть его можно будет только подписавшись на событие TaskScheduler.UnobservedTaskException), но процесс не упадет.
Вариантов обработки исключения несколько:
Обернуть каждый await в отдельный блок try/catch. Плюс этого варианта в том, что вы получите и обработаете оба исключения. Использовать await Task.WhenAll(task1, task2). Минус этого варианта в том, что вы получите информацию только о первом исключении. Использовать Task.WaitAll(task1, task2). Плюс этого варианта в том, что вы получите AggregateException, который будет содержать в себе оба исключения. Минус этого варианта в том, что он блокирует текущий поток.

Как проверить подключен ли компьютер к интернету в .NET

Подскажите какими средствами можно проверить подключение компьютера к сети интернет.


Ответ

Или например таким классом можно воспользоваться.
class InernetChecker { [DllImport("WININET", CharSet=CharSet.Auto)] static extern bool InternetGetConnectedState( ref InternetConnectionState lpdwFlags, int dwReserved);
[Flags] enum InternetConnectionState: int { INTERNET_CONNECTION_MODEM = 0x1, INTERNET_CONNECTION_LAN = 0x2, INTERNET_CONNECTION_PROXY = 0x4, INTERNET_RAS_INSTALLED = 0x10, INTERNET_CONNECTION_OFFLINE = 0x20, INTERNET_CONNECTION_CONFIGURED = 0x40 }
static void Main() { InternetConnectionState flags = 0;
Console.WriteLine( "InternetGetConnectedState : {0} - {1}", (InternetGetConnectedState(ref flags, 0)?"ONLINE":"OFFLINE"), flags ); Console.ReadLine(); } }

Использование статических методов в многопоточном приложении

Обращаюсь к более опытным товарищам. Имеется класс Class1, в нём есть статические методы. Имеются N потоков (больше 1000), которые должны использовать статические методы класса Class1.
Проблема в том, что я любой из этих методов должен выполняться по очереди(как если бы права доступа были разграничены мьютексом). Подскажите как это можно сделать, могу ли я сделать мьютекс приватным членом класса и использовать его? Вопрос "вдогонку", что будет если несколько потоков одновременно "возьмут" статический метод класса?


Ответ

Да, конечно, можно сделать мьютекс приватным членом класса. Если несколько потоков одновременно вызовут статический метод нашего класса, то каждый из них попытается захватить мьютекс, у одного это получится, остальные будут ждать пока этот поток выйдет из метода.
#include #include #include #include
class C { public: static void f() { std::lock_guard lock(static_mutex); // Критическая секция, например выведем идентификатор нашего потока std::cout << std::this_thread::get_id() << std::endl; } private: static std::mutex static_mutex; };
std::mutex C::static_mutex;
int main() { std::vector threads; for (int i = 0; i < 10; ++i) threads.emplace_back([] { C::f(); }); for (std::thread &t : threads) t.join(); return 0; }
Update: Заменил unique_lock на lock_guard и thread.detach() на thread.join(), как советуют в комментариях. А вообще, посмотрите другой ответ, там предложено более изящное решение.

Как правильно проектировать классы с большим количеством обязательных параметров?

Мне требуется создать достаточно массивный класс, координирующий работу ряда объектов. Для его работы необходимо отправить ему около пятнадцати объектов. Без них его работа не возможна, по этому было принято решение отправлять все объекты через конструктор. Естественно, что конструктор на 10+ аргументов выглядит просто ужасно! Есть альтернатива отправлять все объекты посредством свойств, но этот способ имеет свои недостатки. Придётся лепить дополнительные проверки на null и выкидывать исключения. Да и стороннему разработчику, если потребуется работать с таким классом придётся потратить дополнительный усилия, что бы не пропустить никакого важного свойства.
Как быть? Множество аргументов в конструкторе? Или множество свойств обязательных к заполнению?


Ответ

Есть такой анти-паттерн God он же Божественный объект. Его главная особенность — он может всё. Такой объект нарушает принцип единственной ответственности, и, скорее всего, его можно разбить на три или четыре несвязанных класса.
Если посмотреть на ту же ситуацию под другим углом зрения, можно говорить о ней, как об ошибке декомпозиции: перескакивании на несколько уровней вниз. Например, вещество состоит из молекул, молекулы из атомов, атомы из протонов, нейтронов и электронов, протоны и электроны — из кварков. Естественно, можно сказать, что молекулы состоят из протонов и электронов, и это правда, но, пропустив атомы, вы получаете весьма запутанную картину молекулы. Атомы позволяют значительно упростить модель.
Боб Мартин в книге Чистый код описывает один из запахов кода: вызов методов разного уровня детализации. Этот тот случай, когда декомпозируя молекулы, программист иногда работает с ними как с набором атомов, а иногда, как с набором элементарных частиц.
Скорее всего в данном случае можно отказаться от пятнадцати классов, в пользу трёх-четырёх, каждый из которых состоит из трёх-четырёх более мелких классов.
Количество классов у вас увеличится, поскольку появятся промежуточные; зато каждый из них теперь будет простым для понимания.
С другой стороны, бывают ситуации, когда большое количество параметров вполне допустимо. Мартин Фаулер описал паттерн Registry (Реестр), который предоставляет доступ к набору однотипных объектов. Скажем, вам нужно 5 или 6 репозиториев и, вместо того, чтобы передавать их по одному, вы заводите реестр репозиториев и передаёте только один этот объект. При этом у самого реестра в качестве параметров конструктора теперь передаются все репозитории, которые есть в вашей программе. Поскольку все репозитории относятся к одной категории объектов, код реестра получается простым и понятным. При этом конструктор реестра имеет 15-20 параметров и это всё равно выглядят ужасно. Я бы назвал этот случай неизбежным злом, и отнёс бы его причину к ограниченным выразительным средствам языка. В общем, иногда мы просто вынуждены писать так, потому что по другому язык не позволяет. Но будьте осторожны — прежде чем решить, что всё с вашим кодом в порядке, проверьте, что речь не идёт о первом случае: божественном объекте и смешении уровней детализации.

При работе в команде, как правильно подтягивать изменения из веток в git?

Во время работы в команде над одним продуктом возник вопрос - как правильно и корректно подтягивать изменения других коллег с их веток или с ветки dev (основной, скажем так).
Когда я работаешь один, таких вопросов естественно не возникает и мне между двумя моими машинами было достаточно git pull - подтянуть изменения свои на одной машине - продолжить работать на другой и снова закомитить.
Так как все таки правильно? Я вижу три решения:
1) git pull - подтягиваем все изменения и работаем уже с изененными файлами из минусов вижу только то что все изменения отображаются как "мои"
2) git merge - мержить свою ветку с чужими и работать дальше
3) git cherry-pick - вносить в свою ветку коммиты коллег, которые в ветке будут отображаться как "мои" из минусов вижу только долгий и не удобный перебор всех хешей коммитов
Так вот вопрос - как все таки правльно подтягивать изменения из веток других людей?


Ответ

Вопрос субъективный. Когда вам нужно опубликовать код из своей ветки в основную, у вас есть по сути четыере опции:
просто merge вашей ветки в основную; rebase вашей ветки на head основной (линеаризация истории); комбирированный вариант rebase + merge; перенос патчей (cherry-pick).
Все они имеют плюсы и минусы. Выбирают, то что подходит к текущему проекту/нравится команде. Кроме того, они могут использоваться совместно в рамках сложного workflow.
Сами по себе git flow, github flow, gitlab flow и прочие flow не являются серебрянной пулей и на маленьких проектах в небольшой команде могут доставлять больше сложностей, чем удобств.

Как подключить отдельные стили для IE10+?

Microsoft-то объявил, что теперь IE всё поддерживает, но по факту — c флексами беда, с бутстрапом — беда (по разному принимают отступы).
Как можно эти проблемы решить теперь, если условные комментарии отменили в версиях IE10+?


Ответ

вот есть вариант с использоанием яваскрипт
javascript
var doc = document.documentElement; doc.setAttribute('data-useragent', navigator.userAgent);
css
html[data-useragent*='MSIE 10.0'] h1 { color: blue; }
оригинал

CSS-свойства SVG-изображения

Интересует, можно ли как-то при помощи JS или CSS менять свойства сложного SVG-элемента, такие как "scale", "transform" и другие. Нужно применить к элементу с классом gear


Ответ

Изменение размеров и цвета звёздочки при наведении курсора. Используется совместно трансформация scale и translate. Если использовать только одну команду scale звездочка уйдет по диагонали вниз и вправо, поэтому возвращаем её на место -влево и вверх отрицательными значениями translateX(-200000px) translateY(-220000px)

Теперь я понимаю, почему не рекомендуют использовать файлы SVG, нарисованные в Corel. Просто жуткие цифры - сотни тысяч пикселей и соответственно размеры viewBox отличаются от viewport в десять тысяч раз. Соответственно масштаб 1:10000 Это же не карта страны а всего лишь иконка :)

Что такое ContentProvider?

Я правильно понимаю что ContentProvider это некая сущность которая позволяет обращяться к Sql через URI или через специальный интерфейс ContentProvider.
Также она позволяет решать могут ли другие приложения обращяться к базе данных внутри данного приложение если стоит значение exported true?
И еще вопрос когда мы используем SqliteDatabaseHelper мы получается используем ContentProvider программно?
ни как не могу найти нормальную статью или объяснение это сущности ContentProvider!
Везде говорится как его использовать, но нигде нет инфы конкретно что это такое.


Ответ

ContentProvider - это как следует из его названия провайдер некоего контента. Это необязательно SQL данные - это может быть все что угодно.
Например, можно легко представить, что приложение содержит в себе некие данные, ну скажем, приложение игра морской бой и в нем хранятся данные о последних шагах игрока. Теперь стоит задача - высунуть данные о ходах наружу, причем высунуть безопасно. Вот тут то и приходит на помощь ContentProvider. Правила обращения с ContentProvider довольно просты, декларируется Uri типа:
content://[authority]/[path]
рекомендуется использовать в качестве authority имя пакета с префиксом поясняющим суть провайдера, например для нашего морского боя можно было бы применить:
com.my.application.package.seabattledataprovider
смысл path - указании собственно самого запроса, здесь пример запроса на получение второго шага в нашей игре:
content://com.my.application.package.seabattledataprovider/data?step=2
Синтаксис задания Uri задан документом RFC 2396 и является универсальным, класс Uri поддерживает разбор этого синтаксиса.
Для реализации ContentProvider надо написать свой класс наследующий от класса ContentProvider, где основным методом является метод query(), в котором полученный Uri надо распарсить по заданным разработчиком самим правилам и вернуть данные в виде Cursor, который не всегда является SQLiteCursor. Если данные не являются SQL данными, то надо писать собственный класс, который наследует от класса AbstractCursor
Теперь, чтобы получить данные из нашей игры в морской бой, надо из любого места (если провайдер экспортирован, то и из любого другого приложения) вызвать:
context.getContentResolver("content://com.my.application.package.seabattledataprovider/data?step=2", null, null, null, null);
В ответ мы получим курсор с нашими данными.
И еще вопрос когда мы используем SqliteDatabaseHelper мы получается используем ContentProvider программно?
нет.

Динамический и анонимный тип?

Возвращаясь к моему вопросу заданному ранее: Приведение типа к dynamic и ответа данного на него, что привидени типов там никакого нет, и что левая часть примит занчеение правой - т.е на строке:
dynamic instance = new Program();
тип dynamic примет тип Program, то принимая это во внимание и то, что обычно для хранения анононимных типов используется тип var
var instance = new { Name = "Alex", Age = 27 };
Значит, если я использую анонимные и динамические типы в c#, т.е.:
dynamic instance = new { Name="Alex", Age = 27 };
то dynamic примет тип var исходя из выше написанного?


Ответ

Вывод сделан неверно.
var - это НЕ тип (хотя да, в справке он указывается как implicit type). Тип выводится компилятором в момент компиляции.
В случае с анонимными типами: в момент компиляции они получают вполне конкретное имя. Поэтому во время компиляции строка
var instance = new { Name = "Alex", Age = 27 };
будет заменена на что-то вроде
<>f__AnonymousType0`2 instance = new <>f__AnonymousType0`2 { Name = "Alex", Age = 27 };
Следовательно и dynamic будет хранить этот созданный тип.

Br не дружит с flexbox?

Верстал таблицу с помощью flexbox и заметил одну интересную особенность - элемент br ничего не может внутри flexbox.
Пример:
.flexbox { display: flex; flex-wrap: wrap; border: 2px solid red; padding: 2px; } .item { width: 50px; height: 50px; margin: 2px; border: 2px solid blue; } .new-row, br { display: block; width: 100%; height: 0px; }

Перенос с помощью div'a:

Перенос с помощью br:



В примере у div'а и br одни и те же свойства, но div переносит на новую строку элементы, а br - нет.
Почему?


Ответ

Реализация элемента br в CSS очень хорошо известна как тайна. Различные браузеры поддерживают различные свойства для этого элемента, с разным эффектом (хотя все они поддерживают display: none, чтобы удалить br из макета). Сам CSS признает эту странность еще в CSS1, выделив для неё целый подраздел, и даже сейчас в CSS3 он по-прежнему указан.
Эта особенность flexbox не новая; про неё было известно с 2014 года. В основном, в текущих реализациях, br не создаёт основной блок, а вместо этого рассматривается как часть непрерывного текста, как описано в разделе 4 спецификации Flexbox, создавая невидимый элемент, который не может быть стилизован (потому что он невидимый). Это похоже на элемент display: table-cell, вызывающий создание вокруг себя анонимной таблицы, не смотря на то, что вы можете по-прежнему стилизовать элемент display: table-cell - в случае br элемент. Свойства стиля, которые вы применяете, не имеют эффекта, а настройки невидимого элемент выставляются по-умолчанию.
В этом случае, поскольку br (в основном) пуст, и не имеет никакого свойства во flex'е, это приводит к невидимому элементу flex без каких-либо размеров и "прав", поэтому кажется, что br элемент полностью исчез.
Еще в 2014 году CSSWG эта тайна была решена не путем изменения спецификации Flexbox, а просто добавлением специального определения br для css-display-3 для учета поведения, которое мы наблюдаем. Но такое определение не существует в текущей версии спецификации, а также FPWD (который был опубликован после резолюции!), ни HTML-спецификации, ни где-либо еще. Тем не менее, определение выглядит так, как в текущей спецификации css-display-3 (которая не определяет никаких новых свойств, а просто изменяет определение display):
br { content: '\A'; display: contents; white-space: pre; }
...что означает, что элемент br не создаёт основной блок, а просто невидимый встроенный блок, содержащий одну новую строку.
Поскольку это определение по-прежнему отсутствует в css-display-3, я бы пока ещё не стал рассматривать его.
Ответ участника @BoltClock