Страницы

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

вторник, 9 октября 2018 г.

Как установить linux на флешку?

Хочу установить Linux Mint на флешку. Именно установить, а не записать образ.
При записи iso-образа получается, что программы, устанавливаемые при загрузке с флешки, забываются при перезагрузке. Всякие настройки - тоже.
Хочу получить полноценную систему, но на флешке. Как этого добиться?


Ответ

полноценную систему
запустите установку и выберите «флэшку» в качестве целевого диска.
на неё же и загрузчик поставьте на соответствующей стадии установки

Бесконечный цикл for в java

Почему при таком построение цикла for, получается бесконечные вывод нулей?
int i; for (i = 0; i < 10; i = i++) { System.out.println(i); }


Ответ

Ваше выражение: i = i++. Что здесь происходит по шагам:
Сначала i = 0 Вычисляется значение после знака равно. Это ноль: result = i = 0 Вычисляется постфиксный инкремент. Теперь i = i+1 = 1 Результат выражения записывается в i. В итоге: i = result = 0

Можно заменить на: i = ++i. Тогда:
Сначала i = 0 Вычисляется префиксный инкремент. Теперь i = i+1 = 1 Вычисляется значение после знака равно: result = i = 1 Результат выражения записывается в i. В итоге: i = result = 1

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

Для чего нужен файл .gitignore?

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


Ответ

Есть несколько задач, которые наиболее эффективно решаются с использованием файла .gitignore
Не мучаться с выбором нужных файлов для индексации (которая git add).
В большом проекте часто бывает много файлов, которые не подлежат версионированию. Свою лепту вносят редакторы и среды разработки, компиляторы, дебаггеры, прочие инструменты и сама операционная система. А ещё вам может быть удобно хранить какие-то промежуточные результаты в папке tmp
Настройка .gitignore позволяет не выискивать нужные файлы, а добавлять всё сразу или по крайней мере уточнять меньше. Соглашусь с комментарием Etki, подход "просто выбрать нужные" совершенно не масштабируется. Сделать локальный конфиг, который не будет затронут pull-ом
Предположим, для работы вашего проекта нужен конфиг. Значения в нем зависимы от места выполнения, погоды и настроения разработчика.
Вариант 1: сделать local.conf и вносить изменения при необходимости. Если вы его вдруг добавите и закоммитите, то у вас будут конфликты при pull/push. Или с пуллом к вам придёт чужой конфиг.
Вариант 2: версионируемый local.conf.example и игнорируемый local.conf. В работе используется второй, для его формирования вручную или автоматически используется первый. Хорошо, удобно и не препятствует автоматизации. У меня так конфигурируются автотесты, запускаемые локально и на сервере интеграции. Защитить чувствительную информацию от случайного раскрытия
Случается, что вы случайно добавили и закоммитили ключи или пароли от какого-нибудь облачного хранилища, например Amazon. А потом запушили это добро на GitHub. Что нужно делать в такой ситуации? Очень быстро бежать и менять все ключи и пароли, т.к. почти наверняка за ваш счёт уже майнятся биткойны.
Если так уж необходимо хранить чувствительную информацию в папке проекта, то нужно положить её в под-папку, игнорируемую git. Быстро очищать проект от временных файлов
Предположим, что вы пишете на компилируемом языке и при построении вашего проекта формируется множество промежуточных файлов (объектные, прекомпиляция, вот это всё). Перед каждой сборкой необходимо их удалять, чтобы в случае чего не прилинковать лишнее. Можно делать это вручную. Можно написать скрипт. А можно просто добавить их в .gitignore и делать так:
git clean -fX

Оператор | (ИЛИ) при передаче флагов в метод

intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
Вот кусок кода. Метод setFlags на вход получает int, но никак не boolean. Что значить оператор ИЛИ между двумя константами при передаче в метод?


Ответ

| - это побитовое ИЛИ над целочисленными операндами и логическое ИЛИ над булевыми операциями. || - это логическое ИЛИ над булевыми операндами (при этом правый операнд будет вычислен, только если левый вычислялся как false).
Побитовое ИЛИ часто используют при работе с флагами, упакованными в целочисленное значение. Каждому флагу соответствует число вида 2^n. В двоичном представлении это будет одна единица с n нолей слева:
0x01 == 0b00000001 0x02 == 0b00000010 0x04 == 0b00000100 0x08 == 0b00001000 ... 0x80 == 0b10000000
Для получения битовой маски, соответствующей объединению (включению) нескольких флагов, их складывают при помощи побитового ИЛИ.
0b00000001 | 0b00001000 == 0b00001001
Для проверки, включен ли конкретный флаг, используют побитовое И. Если результат ненулевой - флаг включен:
((0b00001001 & 0b00001000) != 0) // true, флаг включен ((0b00001001 & 0b01000000) != 0) // false, флаг выключен

Получение ссылки на экземпляр суперкласса

Пытаюсь разбираться с наследованием в Java. Есть некоторый код:
class A { A getThis() { System.out.println("call getThis() from A"); return this; //(3) } //(3)
Object getSuper() { System.out.println("call getSuper() from A"); return null;} }
class B extends A { B getThis() { System.out.println("call getThis() from B"); return this; }
A getSuper() { System.out.println("call getSuper() from B"); return super.getThis(); } }
class Tester { public static void main (String[] args) { Object a = new B().getSuper(); //(1) System.out.println(a); a = new B().getSuper().getSuper(); //(2) System.out.println(a); } }
В результате на консоль выводится следующий текст:
call getSuper() from B call getThis() from A B@25154f call getSuper() from B call getThis() from A call getSuper() from B call getThis() from A B@10dea4e
Я ожидал, что при отработке строки (1) в a будет лежать экземпляр класса A, а после отработки строки (2) в a будет лежать null. Почему в результате возврата this из строки (3) возвращается ссылка не на родительский класс, а на исходный?

Как я понимаю, при создании экземпляра класса B, в нём хранится ссылка на экземпляр родительского класса A. Я вижу косвенное подтверждение своих слов:
При вызове конструктора класса B происходит вызов конструктора класса A. Даже при переопределении метода f в классе B можно добраться до метода f класса A через конструкцию super.f() Несмотря на это, при работе с экземпляром класса B, я не вижу способа вернуть из него ссылку на экземпляр класса A, который используется в нём. Есть ли какой-то способ всё же реализовать такую возможность?


Ответ

Мне кажется вы путаете понятия Класс и объект.
Класс – это способ описания сущности, определяющий состояние и поведение, зависящее от этого состояния, а также правила для взаимодействия с данной сущностью (контракт).
Объект (экземпляр) – это отдельный представитель класса, имеющий конкретное состояние и поведение, полностью определяемое классом.
Ключевое слово this указывает не на класс, а на экземпляр класса (объект). Ключевое слово super обеспечивает доступ к свойствам и методам этого же объекта но описанным в суперклассе.
Например: Вы создали объект бирюса класса Холодильник, а класс Холодильник наследуется от класса БытоваяТехника. Объект бирюса является и холодильником, и бытовой техникой. this вернет ссылку на объект бирюса, не важно через какой класс получена ссылка Холодильник или БытоваяТехника, объект от этого не меняется.
Так и у вас объект a является экземпляром класса B, одновременно является экземпляром класса А, так как от него наследуется. Когда Вы вызываете метод getSuper() по цепочке в итоге возвращается this, то есть ссылка на объект a
Определения класса и объекта взяты отсюда

Как правильно позиционировать footer на сайте?

На сайте есть header, content и footer блоки. У content на разных страницах разная высота, бывает что высота меньше чем body и тогда шапка находится не внизу страницы а выше. Как правильно позиционировать футер? Нужно ли устанавливать минимальную высоту для content чтобы прижать footer к низу? Хотя минимальную высоту я установить не могу, так как высота на разных устройствах разная. Как поступать в таких случаях?


Ответ

футер прижимается вниз путём его абсолютного позиционирования и вытягивания высоты родительских блоков: html, body и блока с классом .wrapper на 100%. При этом конкретному блоку .content необходимо задать нижний отступ, который равен или больше высоты футера, в противном случае последний закроет часть содержимого.
* { margin: 0; padding: 0; } html, body { height: 100%; } .wrapper { position: relative; min-height: 100%; } .content { padding-bottom: 90px; } .footer { position: absolute; left: 0; bottom: 0; width: 100%; height: 80px; }
Разметка

Text


Как-то так

SVG sprite. Неправильное позиционирование в хроме

Создаю спрайт с помощью gulp (плагин gulp-svg-sprites). В Mozilla отображается нормально, в хроме открываю - всё сдвинуто. Если из конечного svg я генерирую png-спрайт (плагин gulp-svg2png), и подключаю его вместо svg, то во всех браузерах отображается нормально. т.е. я так понимаю это что-то связано именно из интерпретацией браузером.
Как исправить?
Пример моего спрайта после галпа есть здесь . При изменении размеров бегунком в хроме этот баг тоже можно увидеть (хорошо заметно при максимальном увеличении, на белых - когда инвертировать фон).
В мозиле всё нормально.
P.S. Обновил спрайт - установил, чтобы viewBox у всех элементов начинался с "0 0. не помогло.
Настройки gulp:
gulp.task('svg-sprites:build', function () { return gulp.src(options.theme.img_svg_src + '*.svg') .pipe(svgSprite()) .pipe(gulp.dest(options.theme.img_src + 'dict')) .pipe(filter(options.theme.img_src + 'dict/svg/*.svg')) .pipe(gulp.dest(options.theme.img_src + 'dict')); });


Ответ

Короткий ответ
Удалите атрибут viewBox="0 0 41 2023" из SVG. Впрочем, остальные viewBox тоже можете удалить. Можно сделать это автоматически с помощью svgo (плагин removeViewBox).
Длинный ответ
Это похоже на баг Chrome, связанный с расчетом background-size, когда background-size по горизонтали не кратен ширине viewBox в SVG.
Смотрите, у вас background-size задается в em: 4.1em 202.3em (sprite.css:5). А изменение размеров картинок задается через font-size от 2 до 14 px. Исходное значение font-size равно 10px, поэтому при открытии страницы размер фона в пикселях получается 41x2023 px, как раз как viewBox в SVG. Можно прописать background-size: 41px 2023px и все останется по-прежнему.
Но если указать на пиксель меньше: background-size: 40px 2023px, мы увидим смещение по вертикали. Хотя, казалось бы, картинка должна стать просто немного уже.
Поэтому при изменении шрифта все едет. Если задать размер шрифта 11px, то ширина фона получится 4.1em * 11px = 45.1px, а это не кратно 41 (ширина исходного viewBox). Следующее значение шрифта, при котором все показывается нормально - 20px: размер background-size по горизонтали становится 82px, что кратно 41.
Я точно не берусь описать все особенности этого бага (иногда он проявляется, иногда нет), но суть в том, что решение простое: удалите атрибут viewBox="0 0 41 2023" из SVG. Впрочем, остальные viewBox тоже можете удалить.
Можно сделать это автоматически с помощью svgo (плагин removeViewBox). Пример для gulp
gulp.task('remove-viewbox', function() { var svgmin = require('gulp-svgmin');
return gulp.src('src/*.svg') .pipe(svgmin({ plugins: [ {removeViewBox: true} ] })) .pipe(gulp.dest('dest/')); });

Можно ли сделать refresh google banner без перезагрузки страницы?

Мне нужно на сайте обновлять google banner каждых например 3-5 минут. Без рефреша всей страницы и так, чтобы гугл не забанил. Возможно ли такое? Пример, кода банера.


Ответ

Да, можно, пример отсюда
Создаете файл refresh-banner-iframe.php где будет ваш скрипт для AdSense:

Добавляете iframe с сылкой на этот файл:

Для обновления баннера вызываете через javascript:
$url = $('iframe').attr("src"); $('iframe').attr("src","about:blank"); $('iframe').attr("src",$url);
Насчет забанит Google вас или нет не знаю.

Ввод даты в консольном приложении

Пишу программу в С#, которая предполагает простое анкетирование пользователей с последующем выводом данных.
Как правильно организовать в консольном приложении C# получение от пользователя данных о дате его рождения?


Ответ

Сделайте метод, который в цикле опрашивает ввод, пока не будет введено правильное значение:
DateTime inputDoB() { DateTime dob; // date of birth string input;
do { Console.WriteLine("Введите дату рождения в формате дд.ММ.гггг (день.месяц.год):"); input = Console.ReadLine(); } while (!DateTime.TryParseExact(input, "dd.MM.yyyy", null, DateTimeStyles.None, out dob));
return dob; }
Использование:
var dob = inputDoB();
Можно задать произвольный формат вводимой даты. Например, "d.M.yyyy", что позволит вводить дату в виде 1.4.2017

В чем заключается разница между ListView и RecyclerView?

В чем заключается разница между ListView и RecyclerView?


Ответ

RecyclerView был создан в качестве улучшения ListView, так что да, вы можете создать прилагаемый список с контролем ListView, но с использованием RecyclerView проще в нем:
Повторное использование клеток при прокрутке вверх / вниз - это возможно при реализации View Holder адаптера ListView, но это необязательная вещь, в то время как в RecycleView это путь по умолчанию записи адаптера. Разъединяет список из контейнера - так что вы можете поместить элементы списка легко во время выполнения в различных контейнерах (LinearLayout, GridLayout) с установкой LayoutManager . Анимирует общие действия списка - Анимации развязаны и делегированы ItemAnimator . Существует больше о RecyclerView, но я думаю, что эти точки являются основными.
Таким образом, в заключение, RecyclerView является более гибким управлением для обработки «список данных», который следует модели делегирования проблем и оставляет за собой только одну задачу - переработка предметов.

Контейнер deque в STL c++

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


Ответ

А чем вообще один контейнер отличается от другого? И вообще, все их можно заменить обычным массивом...
В конце концов, какая разница, искать ли элемент в контейнере час или пару секунд, или - ну что тут такого страшного, если вставка в начало массива требует перемещения всех его элементов?
Или это существенно?
Тогда учтите, что дек обеспечивает быструю вставку-удаление с обоих концов контейнера - чего, например, не может обеспечить vector, но при этом быстрое (хотя чуть медленнее, чем у vector) обращение к любому элементу внутри, чего, например, не может обеспечить list. Но при этом он не может обеспечить быстрый поиск, как у set или unordered_set...
Ну, а чтобы вам было легче выбирать, какой именно контейнер вам нужен - вот неплохая шпаргалка:

Еще одна сводная шпаргалка по функциям контейнеров:

Что делать, если не работает CSS-класс?

Я пишу css-правило
.nav-users { color: red; }
но на странице нечего не меняется: цвет остаётся серым.


Ответ

Браузеры предоставляют отличные инструменты для отладки css. Не надо ждать, пока кто-то угадает, в чём проблема. Надо просто взять и посмотреть, что же происходит. Покажу на примере Хрома.
Сначала надо обновить страницу со сбросом кэша. В большинстве браузеров это Ctrl + F5
Если не помогло, то по исследуем по следующему плану:
Щёлкнуть проблемный элемент правой кнопкой и выбрать пункт Исследовать элемент
Появится панель отладки, на которой на вкладке Elements выделен кликнутый элемент. Если нужен другой, можно перейти к нему. Затем следует обратить внимание на вкладки Styles и Computed
Если в element.style есть интересующее свойство, то
если в html-разметке прописан стиль в атрибуте style, удаляем оттуда лишнее если нет, кончаем читать этот ответ и переходим к отладке скриптов, которые этот стиль выставляют (либо перебиваем !importantом, что делать крайне не рекомендуется)
На вкладке Styles надо найти свой селектор. Если его там нет, то проблема в опечатке или подключении css-файла.
Как видим, свойство зачёркнуто, но восклицательного знака (говорящего о неверном значении) нет. Это означает, что есть другое правило, имеющее больший приоритет. В простых случаях достаточно посмотреть на вышестоящие правила и понять, что там надо. В более запутанных стоит заглянуть на вкладку Computed и посмотреть, какие вообще значения влияют:
Здесь видно, что селектор .so-header .navigation .-link перебивает наше правило. Кликом по стрелочке можно перейти к самому правилу, но нам это сейчас не нужно.
Теперь мы знаем, что приоритет используемого правила 0 id, 3 класса, 0 тегов Если мы уверены, что наше правило идёт после переопределяемого, то нам достаточно той же силы. Если нет, то надо побольше.
Самый простой способ - это сделать так:
.nav-users.nav-users.nav-users.nav-users { color: red; }
Но руководствуясь здравым смыслом, стоит всё-таки сделать так:
.so-header .navigation .nav-users { color: red; }
или так:
.so-header .navigation .-link.nav-users { color: red; }
Должно заработать:

Если всё равно не работает.
На шаге 6 стоило заглянуть в переопределяющий стиль - возможно, в нём есть !important

В таком случаем нам тоже придётся его использовать:
.so-header .navigation .-link.nav-users { color: red !important; }

(char *)malloc(…)

В некоторых отрывках кода наблюдаю приведение malloc() к (char *), (int *) и т.п. Зачем это делается?
Например:
char *p; p = (char *)malloc(100*sizeof(char));


Ответ

Во времена динозавров в языке С не было типа void *. Идея универсального указателя void * пришла в С намного позже (причем не откуда-нибудь, а из С++). А до того времени в качестве "универсального" указательного типа использовался именно char *. Функция malloc возвращала результат именно типа char *. Именно в те времена и существовала необходимость для выполнения неявного приведения результата malloc от char * к конкретному типу указателя-получателя. В примерах кода, написанных на достандартном С, включая первые версии K&R, это приведение везде присутствует именно по этой причине.
С появлением в языке типа void *, неявно приводящегося к любому указательному типу, и с последующим переходом malloc на void *, необходимость в явном приведении отпала. Однако в большом количестве старого кода его еще можно увидеть. Это приведение перекочевало и во второе издание K&R С, написанное еще до появления первого стандарта С. Несмотря на то, что второе издание задним числом привели в соответствие со стандартом С89/90, примеры кода в нем никто исправлять не стал. В результате студенты, использующие книгу K&R для обучения, "нахватываются" этот странной привычки оттуда, слепо следуя ей как "карго культу".
В современном С-коде выполнять такое приведение имеет смысл разве что только в кросс-компилируемом С-С++ коде, т.к. в С++ указатель void * к другим типам сам по себе не приводится. В частности, например, при реализации кросс-компилируемых макросов и inline-функций в заголовочных файлах.
(Наследием тех времен, когда в качестве универсального указателя в С использовался тип char * в современной спецификации языка является гарантия того, что объектное представление и требования выравнивания типов void * и char * обязаны совпадать.)

Справедливости ради надо заметить, что один аргумент современных любителей явно приводить результат malloc в С коде (не предназначенном для кросс-компиляции) таки содержит определенное рациональное зерно. В коде вроде
T* p; ... p = (T *) malloc(N * sizeof(T));
если кто-то поменяет тип указателя в объявлении p с T * на U *, но при этом забудет исправить выделение памяти через malloc, то в вызове malloc компилятором будет сгенерировано диагностическое сообщение о несоответствии типов указателей. Да, это действительно так.
Однако более грамотным вариантом защиты от подобных проблем будет использование идиомы с "типонезависимым" выделением памяти
p = malloc(N * sizeof *p);
которое само по себе "автоматически" подстроится под вышеупомянутое изменение типа.

В чем различия между Git и SVN?

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


Ответ

GIT распределяется, а SVN - нет. Другими словами, если есть несколько разработчиков работающих с репозиторием у каждого на локальной машине будет ПОЛНАЯ копия этого репозитория. Разумеется есть и где-то и центральная машина, с которой можно клонировать репозиторий. Это напоминает SVN. Основной плюс в том, что если вдруг у вас нет доступа к интернету, сохраняется возможность работать с репозиторием. Потом только один раз сделать синхронизацию и все остальные разработчики получат поолную историю. GIT сохраняет метаданные изменений, а SVN целые файлы. Это экономит место и время. Система создания branches, versions и прочее в GIT и SVN отличаются значительно. В GIT проще переключатся с ветки на ветку, делать merge между ними. В общем GIT я нахожу немного проще и удобнее, но бывают конечно иногда сложности. Но где их не бывает? Разумеется есть гораздо больше отличий, но я перечислил те, которые чаще всего встречаются при работе с репозиториями и на МОЙ взгляд наиболее важные.

Как сосчитать количество определенных элементов в списке c#?

Есть список, состоящий из объектов. Все эти объекты отличаются значением одного поля. Задача состоит в том, чтобы вывести количество объектов для каждого значения этого поля.


Ответ

Пусть список называется list, а поле по которому считаем называется Name, тогда
var result = list.GroupBy(n => n.Name).Select(m => new {m.Key, Count = m.Count()});
Это у нас будет LINQ вариант, на тот случай если не захотите создавать свой "велосипед", а воспользуетесь штатными средствами языка. В противном случае @rdorn дал отличный ответ.

Единожды вызываемая функция

Как при создании объекта класса вызвать функцию, которую в дальнейшем нельзя будет вызывать (ни с помощью данного экземпляра, ни других экземпляров этого класса)?


Ответ

Для этого в C++ есть специальная функция и флаг, делается это так:
std::once_flag flag; //... class Class { Class() { std::call_once(flag, [this]{ SomeMethod(); }); }
void SomeMethod() {...} }
Таким образом мы вызовем SomeMethod() в конструкторе однажды, но это не запретит вызывать этот метод в других местах программы, поэтому его можно сделать приватным, но это не запретит вызов приватного метода в других методах класса. Для того, чтобы полностью исключить повторный вызов какого-либо кода, нужно весь этот код поместить в лямбду, которая передаётся в std::call_once
std::once_flag flag; //... class Class { Class() { std::call_once(flag, [this] { // Тут будет код, который нужно вызывать лишь единожды }); } }

Как выполнить последовательно несколько асинхронных фунций в Javascript без коллбэков?

Как написать простой, понятный, легко обслуживаемый код, который запускает последовательно несколько асинхронных функций в javascript/jQuery? (когда отработает одна, должна запускаться другая)
Следующий пример иллюстриует мой вопрос:
function f1(){ setTimeout( function(){ console.log(1); }, 30); } function f2(){ setTimeout( function(){ console.log(2); }, 20); } function f3(){ setTimeout( function(){ console.log(3); }, 10); } f1(); f2(); f3();
на выходе 3 2 1 как сделать что-бы выдавало 1 2 3 ?
Желательно без коллбеков - т.к. если надо запустить последовательно больше двух функций это уже тяжело читать. Многое говорит о том что возможно решение с помощью обьекта $.Deferred, но пока не видел разумного варианта.
Подобный вопрос задавался не раз, но я почему-то не нашел ответа который бы меня устроил.


Ответ

Если вы хотите использовать Обещания (Promise), то для начала вам нужно модифицировать ваши функции так, чтобы они возвращали Обещания. Например, первая из ваших функций будет иметь вид:
function f1() { return new Promise(function(resolve){ setTimeout(function() { console.log(1); resolve(); }, 30); }); }
Остальные функции преобразуются аналогичным образом.
Теперь у вас есть три функции (f1, f2, f3), которые возвращают Обещания и вы хотите выполнить их последовательно. Если вы не используете библиотек, вроде Bluebird, то вам придется реализовать очередь вызова Обещаний вручную. Это не так сложно, как кажется:
// Аргумент "deeds" - это массив функций, которые должны выполняться // последовательно. При этом, каждая функция должна возвращать // Обещание (Promise). var seqRunner = function(deeds) { return deeds.reduce(function(p, deed) { return p.then(function() { // Выполняем следующую функцию только после того, как отработала // предыдущая. return deed(); }); }, Promise.resolve()); // Инициализируем очередь выполнения. }
А пользоваться этой очередью нужно вот так:
seqRunner([f1, f2, f3]).then(function() { console.log('Done!'); });
А вот и JSFiddle с рабочим примером

Замечание:
Если у вас заранее известное, небольшое число функций, то можно вообще обойтись без функции seqRunner и связывать функции вручную:
f1().then(function() { return f2(); }).then(function() { return f3(); }).then(function() { console.log('Done!'); });

Класс Bundle зачем он?

PageFragment fragment = new PageFragment(); Bundle args=new Bundle(); args.putInt("num", page); fragment.setArguments(args); return fragment;
Вопрос: Зачем Bundle нужен? чтобы создать связку ключ-значение? или у него есть еще функции?


Ответ

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

Зигзаговый border в CSS

Нужно сделать зигзаговую границу блока (из треугольников).
Как это лучше реализовать?


Ответ

.zig-zag { background: #1ba1e2; position: relative; } .zig-zag:after { background: linear-gradient(-45deg, transparent 20px, #1ba1e2 0), linear-gradient( 45deg, transparent 20px, #1ba1e2 0); background-position: left bottom; background-size: 20px 20px; content: ""; display: block; height: 20px; width: 100%; position: absolute; } /* Просто оформление, для зиг-зага не нужно */ body { margin: 0; } h1 { color: #fff; font-family: Arial; margin: 0; padding: 15px; text-align: center; }

Рамка зубцами


Как сделать треугольную стрелку под табом?

Как сделать на css такую стрелку под активным табом?


Ответ

Можно для этого использовать псевдоэлементы с border
// для демонстрации переключения табов $('.block').on('click', function() { $('.block').removeClass('active'); $(this).addClass('active'); }) .block { width: 70px; text-align: center; padding: 20px 0; float: left; border-bottom: #000 solid 1px; position: relative; cursor: pointer; } .block:before, .block:after { content: ''; width: 0; height: 0; border-style: solid; border-color: transparent; position: absolute; left: 50%; top: 100%; margin-left: -10px; transition: all 0.2s ease; border-width: 0px 10px 0 10px; } .block:before { border-top-color: #000; } .block:after { border-top-color: #fff; margin-top: -1px; } .block.active:before, .block.active:after { border-width: 10px 10px 0 10px; }

TAB1
TAB2
TAB3
TAB4

Создание тени, не падающей на div

Всем привет! Я новичок и я пытаюсь создать вот такую вот тень для фотографий:
А получается, к сожалению вот так и самостоятельно решить эту проблему у меня не получается:
Т.е. тень верхней фотографии падает на нижнюю, а этого не должно быть.
К каждой фотографии применяю display:block; position:absolute; и z-index:число;


Ответ

Как вариант решения данной проблемы, могу посоветовать следующий код (в данном случае вам необходимо тень делать у контейнера, содержащего элементы):
.container { filter: url(drop-shadow.svg#drop-shadow); filter: drop-shadow(2px 2px 10px rgba(0, 0, 0, .5)); } .element { position: relative; width: 150px; height: 150px; } .element__one { background-color: lightgreen; } .element__two { top: -70px; left: 40px; background-color: lightblue; } .element__three { top: -140px; left: 80px; background-color: pink; }


Подробнее про идею для решения данной проблемы можно почитать в книге Леа Веру: Секреты CSS в главе 16 "Падающие тени неправильной формы".
Если коротко, то в данном решении используются фильтр SVG, и упрощенный аналог на CSS, и если не поддерживается одно свойство, то будет применено другое.
Про фильтры подробнее можно прочитать тут и тут.

Как восстановить GRUB?

Стоял Linux Mint и Windows 7. Слетела винда. Переустановил Windows и затерся grub. Можно ли без переустановки восстановить загрузочный сектор для linux ?


Ответ

Нужно загрузиться с LiveCD, далее смонтировать корневой раздел установленной Linux системы, например, в папку /mnt
Станем суперпользователем
$ sudo su
Узнаем какие Linux увидел разделы и файловые системы
# fdisk -l /dev/sda # blkid # lsblk
Монтирование корня
# mount /dev/sda3 /mnt
Где sda3 - раздел с корневой файловой системой установленного Linux.
Если содержимое /boot было расположено в отдельном от корневой файловой системы разделе, его тоже необходимо монтировать. Но в начале конечно всегда монтируется корень, после уже файловая система с boot в /mnt/boot Необходимо, если после монтирования корня каталог /boot установленной системы пуст.
Пример монтирования /boot.
# mount /dev/sda4 /mnt/boot/
Потом выполнить монтирование виртуальных ФС
# mount --bind /dev /mnt/dev # mount --bind /dev/pts /mnt/dev/pts # mount --bind /proc /mnt/proc # mount --bind /dev /mnt/dev # mount --bind /sys /mnt/sys
Сменим текущий корень
# chroot /mnt
Вы окажетесь уже в вашей старой системе, после этого выполнить
# grub-install # update-grub2
Выйдем из корня установленной системы
# exit
Первая команда установит загрузчик stage1 grub2 в MBR, вторая обновит список загрузки - выполнит поиск систем и добавит их в меню загрузки.
Далее необходимо размонтировать все файловые системы в обратном порядке
# umount /mnt/sys # umount /mnt/dev # umount /mnt/proc # umount /mnt/dev/pts # umount /mnt/dev # umount /mnt/
Далее выключите компьютер. Включите. Установите загрузку с жесткого диска.
# reboot

Когда использовать scope.$apply();

Чесно, не зря пишут, что это сложная тема для понимания. Я не понял, как и большинство. Где использовать вот эту штуку? Пример: .directive('clickable', function() {
return { restrict: "E",
link: function($scope, element, attrs) { element.bind('click', function() { $scope.$apply(function() { $scope.user++; $scope.bar++; }); console.log('1')
}); } }
}); Без обертки $scope.$apply() пример работать не будет. Не знаю, нашел такую заметку: Важно: любые события браузера вызываются вне области видимости AngularJS, поэтому внутри ваших обработчиков таких событий необходимо вызывать $scope.$apply


Ответ

Так, а что конкретно не понятно? Функции $apply/$digest запускают dirty-check, и если данные в $scope изменились, то обновляют view, вот и вся магия. Когда вы изменяете данные внутри angular, например, на ng-click, он сам вызывает метод $apply, если ваш код выполняется не через angular, то нужно запустить dirty-check руками. Вот очень приблизительные примеры: ng-click внутри себя angular, заменил это конструкцию на: jqLite('button').on('click', function (evt) { evt.preventDefault();
$scope.counter++; $scope.$apply(); }); или $timeout(function () { $scope.foo = "bar"; }, 100);
// эквивалентна setTimeout(function () { $scope.foo = "bar"; $scope.$apply(); }, 100); Всё это очень примерно, но суть в том, что когда вы используете биндинг или методы angular, он за вас вызывает $apply/$digest, вот и всё, ничего сложного тут нет.

Как описать все символы некоторого языка в регулярном выражении? Есть ли готовые решения?

Можно ли полагаться на то, что JVM «умным образом» разрешает диапазон "а-я" или "a-z" в регулярном выражении и добавляет в него все символы некоторого алфавита, который начинается на «а» и заканчивается на «я»?
Ответ: нет, диапазон читается буквально, и это можно подтвердить экспериментом
Символы Ё и ё не попадают в диапазон юникода, в котором лежат все кириллические символы русского языка. То же самое верно для существенной части других алфавитов: буквы в Unicode идут не подряд. Предлагаю отвлечься от особенностей кириллицы и подумать об абстрактном "a-z", где "a" и "z" — первая и последняя буква какого-то алфавита.
Есть ли в Java готовое решение для того, чтобы описать все "родные" буквы в заданной локали?
Ответ: в Java нет, но есть в ответе к этому вопросу
Нашел про поддержку локалей, но не вполне представляю, как это можно применить в регулярном выражении. Логично бы ожидать character class, какой-нибудь \p{locale_Ru_Ru}, но не нахожу.
Навеяно этим ответом к вопросу «Как определить не русский текст?»


Ответ

Регулярки просто включают диапазон между порядковыми значениями символов.
Полный кириллический диапазон безусловно включает Ёё и многое другое. Так что, чтобы собрать базовую кириллицу, достаточно задать диапазон вида [\x400-\x4ff] (не уверен что точный синтаксис для этого формата в Java).

Методы сравнения ссылочных классов в .Net

Ранее считал, что переопределять Equals для своих классов можно и нужно. Но натолкнулся на иную информацию, что переопределение может привести к проблемам с некоторыми коллекциями.
Когда следует переопределять object.Equals() для своих классов? Неужели, если хочется сравнить на эквивалентность согласно сущности два объекта своих классов, нужно создавать отдельный метод? А как быть со сторонними классами? Получается они не поставляют средств сравнения на эквивалентность сущностей?
Что я понимаю под эквивалентностью сущностей. Допустим есть класс
public class Country { public Country (string name) { this.name = name; } private readonly string name; public string Name { get {return name;}} }
public void Main() { var c1 = new Country("Россия"); var c2 = new Country("Россия"); }
так вот c1 и с2 для меня эквивалентны, т.к. не может быть двух стран в моём мире, с одинаковым названием. Просто так получилось, что мы создали два экземпляра, но они идентичны по своей сути..


Ответ

Как известно, .NET по умолчанию сравнивает объекты ссылочных типов по ссылкам, а объекты значимых типов -- побитово (читай, по значению).
К чему это приводит в вашем примере? К тому, что c1 и c2 считаются неравными. С т.з. бизнес-логики вы правильно заметили, что они равны, однако среда ничего не знает о бизнес-логике.
Отсюда выводы:
Метод Equals() надо переопределять там, где требуется, чтобы объекты считались равными по какому-то определенному правилу. В частности, это нужно, когда вы используете объекты типа в качестве ключей словаря, элементов хэш-сета, а также в качестве элемента какой-либо коллекции и вызываете метод Contains(). Также стоит заметить, что в пару к Equals() нужно переопределять и метод GetHashCode() Да, нужно. Потому что правила равенства двух объектов одного типа -- это, грубо говоря, бизнес-правила, то, что относится к вашему приложению. Среда исполнения о них ничего не знает, но о них знаете вы как разработчик. Для кастомного сравнения экземпляров сторонних классов используется интерфейс IEqualityComparer и его реализации. В BCL включены некоторые готовые реализации (например, для сравнения строк без учета регистра). В большинстве случаев вам потребуется создавать свой компаратор.

Непонятный синтаксис

Зачем нужен данный синтаксис, ведь тип результата не вычисляется автоматом?
auto foo(int arg) -> int {}


Ответ

Этот синтаксис появился в результате включения в стандарт C++ лямбда-выражений.
Лямбда-выражение может быть записано, например, как,
auto foo = [](int arg) -> int { /*...*/ };
Это лямбда-выражение может быть преобразовано в функцию, имеющую тип int( int )
Этот синтаксис переняли для объявления функций. У функции в ее начале должен присутствовать спецификатор(ы) типа возвращаемого выражения. Так как реальный тип возвращаемого значения указывается после списка параметров, то в качестве спецификатора возвращаемого значения в объявлении функции используется спецификатор auto
В приведенном вами примере большого смысла так объявлять функцию не имеется. Но иногда тип возвращаемого значения может зависеть от типа вычисления сложного выражения, определить который программисту самостоятельно бывает трудно, да и это может привести к ошибке.
Поэтому этот синтаксис удобен, например, при объявлении шаблонных функций.
Рассмотрите следующую демонстрационную программу.
#include
template auto foo(const T &x, const U &y) -> decltype( x + y ) { return x + y; }
int main() { int x = 10; int y = 20;
std::cout << typeid(foo(x, y)).name() << std::endl;
long z = 30;
std::cout << typeid(foo(x, z)).name() << std::endl;
float f = 40;
std::cout << typeid(foo(x, f)).name() << std::endl; }
Вывод программы на консоль, например, в MS VC++ может выглядеть как
int long float
Заранее сказать, какой будет тип возвращаемого выражения, невозможно. Он зависит от типов параметров функции и от типа вычисляемого выражения. Использование спецификатора типа auto в данном примере облегчает объявление функции.

Различие define и const

В чем различие между этими определениями переменной:
#define N 100
и
const int n = 100;
Что из этих двух является более предпочтительным и в чем достоинства каждого?


Ответ

Это очень дискуссионный вопрос у всех. Но в целом, в с++ лучше использовать const везде, где это возможно.
Плюсы:
на них действуют области видимости. компилятор относится к ним как к обычными переменным, доступным только на чтение. у них есть тип.
У define это все - недостатки. Они действуют от места определения и до ... конца. Хотя, где этот конец будет - ещё нужно хорошо подумать. Они могут переопределять друг дружку и иногда искать, где именно было в очередной раз переопределено - ещё то удовольствие. Компилятор часто выдает очень "загадочные ошибки". Классический пример с макросом min/max. Файл windows.h определяет их. И может быть очень весело. Детали - NOMINMAX
Ещё у define есть большой недостаток - это то, что некоторые думают, что там есть скобки. А их нет.
#define add(a,b) a+b
if (add(x,y) * add(x,y)) {}
кажется, что будет посчитано (x+y) в квадрате. А на самом деле - .... на самом деле x+y + x*y.
Но мы немного отвлеклись. Такого сделать обычным const уже нельзя. Но легко сделать inline функциями. И их результат будет предсказуемый.
Поэтому:
eсли нужно просто определить константу - используйте const (даже само слово намекает). eсли нужно определить макрос - используйте функции (если хочется интересней - используйте inline, но компиляторы сейчас достаточно умные). если нужно усложнить код и добавить синтаскического сахара - тогда самое время использовать define.
UPD
А ещё хорошо найти книгу Эффективное использование C++. 55 верных советов улучшить структуру и код ваших программ - Скотт Майерс и почитать второй совет (в любом случае - там всю книгу можно читать и читать).

Точки в конструкторе java

Есть конструктор
public CustomizedComparator(Comparator... comparators) { this.comparators = comparators; }
Вопрос в том, что значат эти три точки в нем?


Ответ

Это конструктор с переменным числом аргументов типа Comparator
В вашем классе поле this.comparators вероятно имеет тип Comparator[] - то есть массив компараторов.
Вызывать такой конструктор вы можете, например, такими способами:
CustomizedComparator();
CustomizedComparator(comparator);
CustomizedComparator(intCompataror, longComparator, stringComparator);
и так далее.
По сути это является синтаксическим сахаром для передачи массивов в методы/конструкторы.

Ошибка “variable might not have been initialized”

Возник вопрос по логике кода. Почему компилятор не пропускает такой код?
String testStr; if (check > 0) { testStr = "abc"; } System.out.println(testStr != null ? testStr: "0");
При компиляции выдаётся ошибка:
Error:(134, 32) java: ... variable testStr might not have been initialized
Хотя вроде как логичным будет просто вывести "0" в случае, если testStr не было инициализировано... Какая тут логика?


Ответ

Есть два варианта интерпретации данного кода:
Программист хотел объявить переменную с каким-то значением, но допустил ошибку в коде: пропустил присвоение значения либо блок else. В этом случае компилятор поможет ему найти ошибку. Программист полагает, что testStr будет инициализировано как null по умолчанию. В этом случае компилятор заставит программиста инициализировать переменную как null явно.
Разработчики Java пришли к выводу, что:
важно отловить возможные ошибки; неудобства, связанные с инициализацией локальных переменных, незначительны.
Обязательная инициализация локальных переменных определена в спецификации Java
Chapter 16. Definite Assignment Each local variable (§14.4) and every blank final field (§4.12.4, §8.3.1.2) must have a definitely assigned value when any access of its value occurs. An access to its value consists of the simple name of the variable (or, for a field, the simple name of the field qualified by this) occurring anywhere in an expression except as the left-hand operand of the simple assignment operator = (§15.26.1). For every access of a local variable or blank final field x, x must be definitely assigned before the access, or a compile-time error occurs. Перевод (из книги «Язык программирования Java SE 8. Подробное описание») Глава 16. Определенное присваивание Каждая локальная переменная (§14.4) и каждое пустое final-поле (§4.12.4, §8.3.1.2) должны иметь определенно присвоенное значение при любом обращении к их значениям. Обращение к их значениям состоит из простого имени переменной (или, в случае поля, простого имени поля, квалифицированного ключевым словом this), находящегося в любом месте выражения, за исключением левого операнда оператора присваивания = (§15.26.1). Для каждого обращения к локальной переменной или к пустому final-полю x, x должно быть определенно присвоено до этого обращения, иначе генерируется ошибка времени компиляции.
Посмотрите также обсуждения этого вопроса в английской версии:
Why are local variables not initialized in Java? Why must local variables, including primitives, always be initialized in Java?

В чём смысл создания массива на 0 элементов

int[] indexOfTask = new int[0];
С какой целью такая возможность поддерживается компилятором?


Ответ

Есть такая замечательная книга "Effective Java" Джошуа Блоха, содержащая обширный список рекомендаций по проектированию и разработке эффективных, надёжных и легкосопровождаемых программ. Одна из рекомендаций звучит как "Возвращайте массивы и коллекции нулевой длины, а не null". Например, в классе java.io.File есть метод listFiles(), который возвращает массив файлов каталога. Представьте, как неудобно было бы, если бы он не смог вернуть массив нулевой длины для пустых каталогов! Пришлось бы вместо лаконичного
for (File file : dir.listFiles()) { ... }
делать
File[] files = dir.listFiles(); if (files != null) { for (File file : files) { ... } }

Кириллица в именах переменных

Как известно, кириллица в именах переменых это плохо. А почему? Ну, кроме известного довода, что "а если вы будете работать с иностранными программистами?" P.S. Предположим, что речь идет о языке полностью поддерживающем Юникод.


Ответ

Даже без иностранных программистов можно найти несколько относительно веских причин: Русские слова обычно длиннее английских ("получить" - "get", "страница" - "page", ну и так далее). Это, конечно, не всегда так, но в целом - соблюдается Необходимость постоянного переключения раскладки Каша из русских и английских слов визуально плохо выглядит

Агрегация и композиция

Здравствуйте!
Всегда считал, что агрегация — это синоним композиции, однако наткнулся на блог в интернете, где приводятся отличия композиции от агрегации
Мне это снесло крышу. Поясните, пожалуйста, плюсы/минусы и того и другого на небольших примерах. Как это влияет на расширяемость, тестируемость и т. д.


Ответ

Существует несколько видов взаимодействия объектов, объединенных под общим понятием "Has-A Relationship" или "Part Of Relationship". Это отношение означает, что один объект является составной частью другого объекта.
Существует два подвида этого отношения: если один объект создает другой объект и время жизни "части" зависит от времени жизни целого, то это называется "композиция", если же один объект получает ссылку (указатель) на другой объект в процессе конструирования, то это уже агрегация.
Давайте рассмотрим пример из .NET Framework, чтобы увидеть, какие ограничения/последствия несут эти отношения: StringWriter + StringBuilder
Класс StringWriter - это специализированная версия класса TextWriter, которые активно используются при сериализации и для получения текстового представления объектов.
Конкретно StringWriter создает строковое представление объекта или графа объектов и опирается на экземпляр StringBuilder в своей работе. Т.е. мы можем сказать, что 'StringWriter HAS A StringBuilder' или 'StringBuilder is part of StringWriter'. Теперь давайте посмотрим, как решить, должен ли StringWriter получать экземпляр StringBuilder-а извне или создавать его?
С одной стороны, нам, как клиенту класса StringWriter часто бывает все равно, что именно используется внутри этого класса для получения строкового представления. Это значит, что с точки зрения простоты использования лучше, чтобы StringWriter создавал экземпляр StringBuilder-а самостоятельно.
Но, с другой стороны, конкретный объект StringWriter-а может отвечать лишь за получения части строкового представления, а другая часть строки может вычисляться другим способом. С этой точки зрения, лучше, чтобы StringWriter принимал экземлпяр StringBuilder-а в конструкторе. Это же справедливо и для высоконагруженных систем, в которых разумно использование пула объектов.
Поскольку StringWriter - это библиотечный класс, который должен поддерживать оба сценария, то у него есть перегруженные версии конструктора: один из них создает StringBuilder внутри, а другой - принимает его снаружи.
Другими словами, выбор между композицией и агрегацией основан на соблюдении балланса между различными требованиями в дизайне:
Композиция: объект A управляет временем жизни объекта B
Плюсы:
Композиция позволяет скрыть отношение использования объектов от глаз клиента. Делает API использования класса более простым и позволяет перейти от использования одного класса, к другому (например, StringWriter мог бы поменять реализацию и начать использовать другой тип, например CustomStringBuilder).
Минусы:
Отношение достаточно жесткое, поскольку один объект должен уметь создавать другой: он должен знать конкретный тип и иметь доступ к функции создания. Композиция не позволяет использовать интерфейсы (без привлечения фабрик) и требует, чтобы класс имел доступ к конструктору другого класса: представьте, что конструктор StringBuilder-а является внутренним или же это интерфейс IStringBuilder и только клиенский код знает, какой экземпляр должен использоваться здесь и сейчас.
Агрегация: объект А получает ссылку на объект B
Плюсы:
Более слабая связанность между объектом и его клиентом. Теперь мы можем использовать интерфейсы и одному объекту не нужно знать, как именно создавать другой объект. Большая гибкость. Вытекает из первого пункта
Минусы:
Выставление наружу деталей реализации. Поскольку клиент класса должен предоставить зависимость в момент создания объекта (передать экземпляр StringBuilder-а в момент создания StringWriter-а, то сам факт этого отношения становится известен клиенту. Из первого пункта вытекает увеличение сложности в работе клиентов, а также большая "жесткость" решения в долгосрочной перспективе. Теперь автор класса TextWriter уже не может принять решение самостоятельно и перейти от StringBuilder-а к чему-то другому. Да, можно "добавить еще один уровень абстракции" и выделить интерфейс IStringBuilder, но разорвать это отношение совсем будет невозможно без поломки всех существующих клиентов.
В заключении: дизайн - это поиск компромисса между различными факторами. Композиция проще с точки зрения клиентов класса, но налагает определенные ограничения: "целое" должно уметь создавать "составную часть". Агрегация гибче, но налагает другие ограничения: теперь "целое" не скрывает о существовании "составной части", а значит и не сможет заменить ее на другую "составную часть" в будущем.
P.S. Если очень хочется использовать пример из реального мира, то для объяснения композиции и агрегации может подойти ... отвертка. Если отвертка цельная, т.е. ручка и насадка намертво связаны друг с другом, то мы имеем отношение композиции. Если же насадка съемная и может существовать без ручки или же использоваться с другой ручкой, то мы имеем отношение агрегации

Книга по программированию ASP .NET MVC 4 на VS 2012 [дубликат]

На данный вопрос уже ответили: Книги и учебные ресурсы по C# 1 ответ Друзья! Подскажите, пожалуйста, книгу по программированию ASP.NET MVC 4 на VS 2012 на русском языке, если таковые уже имеются. Спасибо!


Ответ

Могу посоветовать онлайн-книгу по ASP.NET MVC 4 - http://metanit.com/sharp/mvc/index.php больше на русском не встречал. На английском уже есть три книги. Кроме вышеописанной в предыдущем посте: Programming ASP.NET MVC 4: Developing Real-World Web Applications with ASP.NET MVC и ASP.NET MVC 4 in Action Первая печатная книга на русском языке по этой теме выйдет только в следующем году

Перегрузка оператора присваивания C++

Не могу разобраться, как перегрузить оператор присваивания. С бинарными операторами более-менее всё понятно, там хотя-бы два операнда, а вот с этим - никак. Не могли бы вы привести пример перегрузки "=", и разъяснить что где делает, и результат перегрузки? Например перегрузить так, чтобы он к присваиваемому числу прибавлял + 5, или что-то вроде, и показать, к чему и от чего присваивается и к чему где прибавляется.


Ответ

Vector& Vector::operator=(Vector& v)//перегрузка { x=v.x;y=v.y;z=v.z; return *this;//возвращаем ссылку на текущий объект } Не знаю, что вызвало сомнения и вопросы. Главное после присваивания вернуть ссылку на текущий объект.

Область применения Си и Си++

Хотелось бы узнать каковы области применения языков Си и Си++ и для каких платформ чаще их используют?


Ответ

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

Как прописать :hover внутри style=' '?

Нет возможности подключить стили Нет возможности вставить стили между

Dark gray

Как прописать ему "ховер", к примеру background: #555; прямо внутри style=""?


Ответ

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

Dark gray

Как отменить git init в уже существующем репозитории?

В папке с git-ом случайно нажал на git init , в итоге теперь когда делаю git status у меня все файлы отображаются как измененные.
В git log последние коммиты остались. Как отменить действие команды git init?


Ответ

Если просто случайно создали репозиторий, то нужно удалить папку .git в корне. Это полностью уничтожит репозиторий и, разумеется, отменит то, что сделал git init. Через *nix-консоль это делается так:
rm -r .git
Если же Вы сделали git init в уже существующем репозитории, то бояться нечего
Running git init in an existing repository is safe. It will not overwrite things that are already there. The primary reason for rerunning git init is to pick up newly added templates (or to move the repository to another place if --separate-git-dir is given).

Насколько верна такая реализация многопоточности?

Не знаю, корректно ли на этом сайте такие вопросы задавать... В общем я не уверен что код ниже верно написан. Свою задачу он выполняет, проверял неоднократно. Однако меня гложут сомнения в том, насколько эта реализация может считаться правильной. Быть может на C# такого рода задачу принято реализовывать иначе? Хотелось бы получить критику и разъяснения.
Сама программа - аудио конвертер. Ищет в папке подходящие файлы и запускает внешний, консольный конвертер для их преобразования в wav.
public partial class Form1 : Form { static object locker = new object(); string[] findFiles; int fifi = -1;
public Form1() { InitializeComponent(); Conv(); {
async void Conv() { findFiles = Directory.GetFiles(Application.StartupPath, "*.ape", SearchOption.AllDirectories).Union(Directory.GetFiles(Application.StartupPath, "*.ogg", SearchOption.AllDirectories)).ToArray();
// Поддерживаем не более 8 ядер. int p; if (Environment.ProcessorCount < 8) p = Environment.ProcessorCount; else p = 8;
int t; // Если количество файлов больше чем ядер, то устанавливаем столько потоков, сколько ядер. if(findFiles.Length > p) t = p; else // Если файлов меньше чем ядер, то потоков устанавливаем сколько файлов. t = findFiles.Length;
// Устанавливаем количество потоков. Task[] tasks = new Task[t];
// Запускаем потоки. for (int i = 0; i <= p - 1; i++) { if(i <= findFiles.Length - 1) { tasks[i] = new Task(() => ConvertDoWork(++fifi)); tasks[i].Start(); } }
// Ждём выполнения всех задач. await TaskEx.WhenAll(tasks);
Application.Exit(); }
int p, pc; public void ConvertDoWork(int num) { int n = num;
if ((findFiles.Length == 0) || (n > findFiles.Length)) return;
string s = findFiles[n];
ProcessStartInfo startInfo = new ProcessStartInfo(); if (Path.GetExtension(s).Equals(".ogg")) { startInfo.FileName = "oggdec.exe"; startInfo.Arguments = "-Q \"" + s + "\""; } else if (Path.GetExtension(s).Equals(".ape")) { startInfo.FileName = "MAC.exe"; startInfo.Arguments = "\"" + s + "\" \"" + Path.ChangeExtension(s, ".wav") + "\" -d"; } startInfo.WorkingDirectory = Application.StartupPath; startInfo.CreateNoWindow = true;
Process processReg = new Process(); processReg.StartInfo = startInfo; processReg.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; processReg.Start(); processReg.WaitForExit();
lock (locker) File.Delete(s);
int percentComplete = (int)Math.Round((double)(100 * n) / findFiles.Length); pc = percentComplete / 10;
try { this.Invoke((MethodInvoker)delegate { progressBar1.Value = percentComplete;
// Если не выбрано не отображать прогресс, то отображаем всплывашку. if ((notifyIcon1.Visible) && (!chkNotNotify.Checked) && (!pc.Equals(p))) { notifyIcon1.ShowBalloonTip(300, "Прогресс", "Выполнено " + percentComplete + "% работы", ToolTipIcon.Info); p = pc; }
});
} catch (System.Exception ex) {
}
lock (locker) ++fifi; if (fifi < findFiles.Length) ConvertDoWork(fifi); }


Ответ

1.
Слишком много переменных уровня класса. Каждую из них надо отдельно перепроверять - а мне лень. Куда безопаснее локальные переменные и параметры функции.
2.
Переменная fifi используется опасным образом. Лучше от нее вовсе отказаться, а распараллеливание делать другими методами.
Для распараллеливания через задачи я сам использую и всем рекомендую вот такую конструкцию:
await TaskEx.WhenAll( from file in findFiles select Task.Run(() => ConvertDoWork(file)) );
Вам, я так понимаю, нужно еще ограничить число число одновременно выполняемых задач числом ядер. Вручную это можно сделать вот так:
class LimitedConcurrencyGuard { private readonly object _lock = new object(); private readonly Queue> queue = new Queue>(); private int slots;
public LimitedConcurrencyGuard (int slots) { this.slots = slots; }
public async Task AcquireSlot() { TaskCompletionSource tcs; lock(_lock) { if (slots > 0) { slots--; return new Slot(this); } tcs = new TaskCompletionSource(); queue.Enqueue(tcs); } await tcs.Task; return new Slot(this); }
private class Slot : IDisposable { private readonly LimitedConcurrencyGuard owner; public void Slot(LimitedConcurrencyGuard owner) { this.owner = owner; }
public void Dispose() { TaskCompletionSource tcs; lock(owner._lock) { if (owner.queue.Count > 0) tcs = owner.queue.Dequeue(); else { owner.slots++; return; } } tcs.SetResult(null); } } }
// ...
var guard = new LimitedConcurrencyGuard(Environment.ProcessorCount); await TaskEx.WhenAll( from file in findFiles select Task.Run(async () => { using (await guard.AcquireSlot()) ConvertDoWork(file); }) );
Если поискать в гугле по запросу "ограничение числа одновременно выполняемых задач" - наверняка найдутся и другие реализации. Например, реализующие ограничение на стороне планировщика задач.
Или же можно сначала побить исходную последовательность файлов на куски, а потом запустить их параллельно:
static IEnumerable> SplitAndBatch (ICollection items, int batches) { var count = items.Count; var it = items.GetEnumerator(); while (count > 0) { var current = (count + batches - 1) / batches; // Это на самом деле частное, округленное вверх count -= current;
var batch = new T[current]; for (var i=0; i// ...
await TaskEx.WhenAll( from batch in SplitAndBatch(findFiles, Environment.ProcessorCount) select Task.Run(() => { foreach (var file in batch) ConvertDoWork(file); }) );
В любом случае, логику разделения файлов по ядрам процессора лучше вынести за пределы метода ConvertDoWork - это называется "разделение ответственности".
А может быть, нужно вообще отказаться от использования задач и использовать Parallel API - где ограничение по числу ядер уже реализовано.
3.
Уведомление пользователя о текущем прогрессе - дело хорошее, но сделано оно на устаревшей технологии. Элементарно: если пользователь закроет окно - то метод Invoke вылетит с ошибкой.
Для уведомлений о прогрессе есть такие классы как IProgress/Progress
public void ConvertDoWork(string file, IProgress progress) { // ... progress.Report(null); // ... }
// ...
progressBar1.Maximum = findFiles.Length; var progress = new Progress(_ => { progressBar1.Value = ++done; }); await TaskEx.WhenAll( from file in findFiles select Task.Run(() => ConvertDoWork(file, progress)) );
Достоинство данного способа - в том, что
у метода ConvertDoWork забирается лишняя ответственность (индикация хода выполнения); класс Progress берет задачу перехода обратно в поток UI на себя.
Кстати, как вам такая идея - считать файлы не по 1 каждый, а пропорционально их размеру?..
4.
Формирование Arguments лучше делать через string.Format - это куда проще в восприятии.
5.
Лучше не использовать Application.Exit просто так: если в дальнейшем понадобится использовать старый код как часть большего проекта, то придется больно и мучительно искать все места, где он может закрыть всю программу.
В данном случае достаточно просто закрыть форму.
PS по поводу net 4.0
Многие замечательные классы реализованы в проектах Mono и .NET Core.
Лицензии у них свободные, код открытый - так что 1-2 класса (тот же Progress) зачастую можно прямо оттуда скопировать к себе в проект. Только если проект будете распространять - то с лицензиями придется разобраться.

Разница между push_back и emplace_back

Подскажите, в чем разница между старым добрым vector::push_back и пришедшим в новом стандарте vector::emplace_back?


Ответ

push_back добавляет копию объекта (или обеспечивает перемещение, если возможно), а emplace_back создает объект непосредственно в конце вектора, т.е. без лишнего копирования (или перемещения).

Почему не стоит юзать sudo pip?

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


Ответ

Это актуально не только для pip, использование прав суперпользователя может быть черевато выполнением произвольного кода, который может навредить системе.
Если не получается установить какой-то пакет без root'а, то, вероятно просто неверно выставленно разрешение на директорию с пакетами python

Какой смысл в символе “Т” в стандартном формате записи даты-времени?

Какой смысл в символе "Т", например, вот в такой записи даты/времени: 2017-12-27T12:59:04.723 ?


Ответ

Символ T - это разделитель даты и времени в строке, описывающий время. Определён в стандарте ISO 8601. См. раздел Combined date and time representations

Олимпиадная задача с повышенной сложностью

Салют всем чайникам и гуру, девелоперам и фрилансерам, прогерам и кодерам! Но речь не о классификации и иерархии нашего брата. Речь о олимпиадной задачи, которую я уже неделю не могу решить. Я точно знаю: решение что-то простое и гениальное - но вот под каким камнем оно спряталось? Так что, суровые хэшкод-юзеры, прошу у вас помощь в решение этой задачи! Задача: Язык программирования: С++. В программе должна быть 1 единственная функция вывода информации, которая может только писать, т.е. не может удалять текст, а только вставлять новый. При этом она работает следующий образом Исполняемый код: func( "1" ); Вывод: (1) [1] Исполняемый код: func( "1" ); func( "2" ); Вывод (1) (2) [2] [1] Испоняемый код: func( "1" ); func( "2" ); func( "qwer" ); Вывод (1) (2) (qwer) [qwer] [2] [1] Я уверен, что задача решается легко, но у меня нет других вариантов, кроме как все же буферизовать хвост, изменять его, и заменять старый хвост на новый. Но тогда приходится использовать удаление символов. P.S. задача не совсем олимпиадная, эта задачка была давно в какой-то мощной статье по альтернативному программирование где рассматривалось несколько примеров, но вот код реализации такой задачи не помню. И статью ту тоже не могу найти. У самого без удаление символов написать не получается.


Ответ

Испоняемый код: func( "1" ); func( "2" ); func( "qwer" ); Вывод (1) (2) (qwer) [qwer] [2] [1] то есть нужно напечатать в обычном и обратном порядке ???? как я понял можно только вызывать одну и ту же функцию... (если да то решение ниже) делаем класс func и в нём храним std::vector делаем конструктор func(string) с записью дубля в вектор и выводом на экран в деструкторе выводим вектор в обратном порядке UPD:: ну ещё будет "красиво" перегрузить "operator()" дабы не портить конструкторы

Какие существуют игровые движки на Java?

Ни для кого не секрет, что игры создаются на различных движках. Все зависит от того, каким языком программирования вы пользуетесь. Например, со знанием C++ для вас возможно программирование при поддержке таких движков, как CRYENGINE или Frostbite, на C# и JavaScript'е вы можете программировать в движке Unity3D.
А в каком движке нужно работать, чтобы создавать игры на Java?


Ответ

Все движки, рассмотренные здесь, распространяются бесплатно 3D Движки jMonkey Engine. Отличная производительность, кроссплатформенность, свой SDK+редакторы, дружелюбное сообщество (вот только англоязычное). Частые обновления и отличная поддержка со стороны разрабов самого движка не может не радовать. Есть модификация версии от 2009 года, её авторы гордо именуют свой мод отдельным движком - Ardor3D (но вот только проект загнулся, какая досада). Даже официальный сайт модификации уже не работает, а перенаравляет на личный блог разработчиков. "Под капотом" стоит LWJGL (Light-Weight JavaGL), поддержка OpenGL v2+ и шейдеров. Плюшки - поддержка всякой лабуды типа Oculus Rift; возможна разработка Android-приложений. Мой вывод: если готовы приступить к более-чем-любительской разработке серьёзных проектов, то jME - ваш выбор. Официальный сайт Приложения на jME Блог разработчика Ardor3D Bonzai Engine. Великолепные утилиты для разработки, но нет как и документации, так и сообщества. Немного отойду от темы и проведу небольшое сравнение сообществ движков jME и Bonzai Engine: в одном только Твиттере у jMonkey Engine 1725 подписчиков, а у Bonzai Engine всего 12. Продолжу: кроссплатформенность (Windows/Linux/Android), OpenGL v2+. Плюшки: редакторы есть даже на Android. Также движок обещает поддержку большого количества форматов моделей. Апдейт: документация внезапно появилась (просто я полтора года назад изучал этот движок, но документации не было вообще). Достойных (да и просто) проектов на этом движке я пока не встречал. Мой вывод: обёртка классная, а начинка оставляет желать лучшего. Официальный сайт Приложения на Bonzai Engine: ??? jPCT. Врядли библиотеку размером в 300 кб можно назвать движком, но всё же это лёгкий инструмент для создания простейших приложений. Есть неофициальный редактор, куча подробной документации и довольно большое дружелюбное(!) сообщество (был случай, что на форуме мне помогал сам создатель движка). Также есть поддержка Android (версия jPCT-AE). За "плечами" движка имеется куча приложений, созданных пользователями (лично я сам залипаю в одну игрушку). Прошу учесть, что такой "движок" не даст такую великолепную картинку, как, например, jME и Bonzai Engine, но тут FPS держится выше и стабильнее. Плюшки: поддержка скелетной анимации. Мой вывод: "дёшево и сердито". Официальный сайт Приложения на jPCT Если кому интересно, то вот игрушка, в которую я залипаю 2D Движки Slick2D. Хороший производительный движок с открытым исходным кодом (лицензия BSD). Кроссплатформенность (Windows/Linux/Mac), поддержка OpenGL v2+. Плюшки: модуль физики jBox2D; легко делается GUI. Официальный сайт Приложения на Slick2D Golden T Game Engine. Движок уже стар (2004), но всё же имеет право на существование. По аналогии с jPCT сгодится для простейших приложений. Поддержка OpenGL v1+. Несмотря на то, что движку уже 11й год, его форумы и поддержка всё ещё работают. Официальный сайт Приложения на GTGE PulpCore. Open-source движок с неплохой производительностью. Как я понял, работает только в плагинах, но планируется сделать этот движок рабочим и на десктопах. Плюшки: тонна туториалов; движок заточен для работы с анимацией. Официальный "сайт" (Google Code) Несколько приложений-примеров на PulpCore

Есть ли у vim какие-то уникальные фичи и уникальная предметная область?

Ради чего сейчас стоит учиться пользоваться vim? Кроме ощущения собственного могущества конечно. Порог вхождения у vim довольно высок, более user-friendly альтернатив - множество.
Есть ли какие-то платформы, области применения, ради которых стоит научиться работе именно с vim?
Более конкретно, имеет ли это смысл для того кто 90% времени работает на винде?
Внимание, конкретный вопрос, на который можно дать однозначно правильный ответ: Есть ли у этого редактора какие-то уникальные фичи и уникальная предметная область?


Ответ

Более конкретно, имеет ли это смысл для того кто 90% времени работает на винде?
Не очень. Учитывая плачевность текущего состояния терминала винды... (в 10 обещают поправить)
Вим нужен для:
Для тех, кто хочет выпендриться. Да, есть у меня пара таких товарищей. Знаете, как они вставляют кусок кода в вим? Они открывают gedit (стандартный редактор ubuntu), там нажимают ctrl+v, потом открывают вим снова... Для тех, кто удаленно работает с unix системами. Никогда не работал с серверами на windows, но если есть доступ по ssh и нужно поправить какой-то конфиг, знание vim может стать бесценным. Для тех, кто пишет что-то низкоуровневое. Опять же, преимущество vim — он есть в каждой *nix. Если вы случайно убили графический интерфейс, gcc выдает ошибку при попытке пересобрать систему, а переустанавливать нельзя, потому что иначе потеряются важные файлы, vim скорее всего запустится и вытащит вас из этой ситуации. Для тех, кто его уже освоил. Говорят, скорость работы после запоминания хоткеев потрясающая. Ну не знаю, я, наверное, не труЪ, потому что clion и pycharm с их множественным выделением, статическим анализом и умением распознавать контекст по-моему увеличивает продуктивность (и качество кода) куда сильнее.
Как итог, скажу следующее: vim может сослужить хорошую службу, весьма полезен, но использовать его как постоянный инструмент для кодинга я не стал (и не собираюсь). Пройдите его обучение (команда vimtutor ru), напишите в нем пару олимпиад или чего вам хочется. Если понравится — продолжайте, а если не особо, просто помните базовые команды, когда-нибудь пригодится.

Зачем вообще нужна куча?

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


Ответ

Думайте о куче как о структуре, из которой можно в любой момент попросить кусок памяти, и в которую можно в любой момент эту память вернуть.
Со стеком такое не получится: в нём вы можете вернуть объект назад только после того, как все предыдущие объекты вернулись.
Например, в гипотетическом языке, в котором есть лишь стек, вот такая конструкция:
object* function() { aux_object* ao = new aux_object(); object* o = new object(); o -> copySettingsFrom(ao); delete ao; return o; }
была бы невозможна, потому что освободить ao нельзя было бы до освобождения o!

Выполнение программы из .ini

Допустим мы в ini файл пишем вот это:
repeat (8) { click(10,10) pause(100s) click(10,10) repeat(2) { pause(10) } }
что то вроде кода самого обычного кликера, по типу UOpilot.
Как обрабатывать и анализировать циклы, вложенные циклы?


Ответ

Окей, ну что же, вам нужен интерпретатор. Давайте-ка разомнёмся и этот самый интерпретатор напишем.
Для начала, нужно составить формальную грамматику вашего языка. Исходя из того, что вы привели в качестве примера программы, можно предположить следующую грамматику:
program ::= statement* EOF compound-statement ::= '{' statement* '}' statement ::= repeat-statement | function-call | compound-statement repeat-statement ::= "repeat" "(" number-constant ")" compound-statement function-call ::= ident "(" arglist ")" arglist ::= EMPTY | arg ["," arg]* arg ::= number-constant | duration-constant
Мы видим, что грамматика простая, а значит, её легко распарсить либо вручную, либо через стандартную связку lex/yacc (flex/bison). Парсить через lex/yacc намного проще, и технической работы намного меньше, но давайте напишем парсер вручную, это будет полезнее.
Итак, поскольку наша грамматика LL(1) (леворекурсивная, а для определения типа следующего правила достаточно подсмотреть на 1 токен вперёд), напишем простейший парсер рекурсивного спуска (recursive descent parser).
Я попробую написать парсер достаточно простой идейно, чтобы его было легко понять, но с другой стороны достаточно общий, чтобы его можно было легко расширить.
Первая часть любого уважающего себя парсера — токенизатор. Мы хотим разделить входную строку на осмысленные токены, чтобы не возиться с отдельными символами.
Для начала, какие у нас есть токены? Это
ключевые слова
repeat пунктуация
круглые скобки фигурные скобки запятая идентификаторы (имена функций) константы
числа (наподобие 10) продолжительность (наподобие 100s)
Это даёт такой enum
enum token_type { // keywords tt_repeat,
tt_ident, tt_number, tt_duration,
// punctuation tt_lparen, tt_rparen, tt_lbrace, tt_rbrace, tt_comma,
// end of file tt_eof,
// parse error tt_error };
и тип данных для токена:
struct token { token_type type; string string_value; long num_value; };
Сам токенизатор будет предоставлять функциональность «подсмотреть» на один токен вперёд, и будет запоминать текущую позицию токена в тексте для удобства отладки:
class tokenizer { const string text; // весь текст int curridx; // текущая позиция в тексте int endidx; // длина текста int currline, currcol; // текущая строка/номер символа в строке // (нужны лишь для отладки)
token lookahead; // следующий токен
void set_lookahead(); // перейти к следующему токену // и запомнить его в lookahead
public: tokenizer(string text) : text(text), curridx(0), endidx(text.length()), currline(1), currcol(1) { lookahead.num_value = 0; lookahead.string_value = ""; set_lookahead(); }
token peek_next() { return lookahead; } void move_ahead() { set_lookahead(); } };
Основная логика токенизатора находится, понятно, в функции set_lookahead. Эту функцию обычно строят на регулярках, наподобие того, как это делает lex. Но мы напишем её вручную.
void tokenizer::set_lookahead() { // начнём с пропуска незначащих пробелов while (curridx < endidx && isspace(text[curridx])) { // не забываем следить за нашей позицией в файле if (text[curridx] == '
') { currcol = 1; currline++; } else { currcol++; } // переходим к следующему символу curridx++; }
// тут мы точно знаем, где начинается наш следующий токен lookahead.lineno = currline; lookahead.colno = currcol; if (curridx == endidx) // конец файла { lookahead.type = tt_eof; return; }
char c = text[curridx];
// с пунктуацией всё просто if (c == '(' || c == ')' || c == '{' || c == '}' || c == ',') { lookahead.type = (c == '(') ? tt_lparen : (c == ')') ? tt_rparen : (c == '{') ? tt_lbrace : (c == '}') ? tt_rbrace : tt_comma; curridx++; currcol++; return; }
// если токен начинается с буквы, это ключевое слово или идентификатор if (isalpha(c)) { // отделим-ка его сначала в переменную string result; while (curridx < endidx && isalpha(text[curridx])) { result += text[curridx]; curridx++; currcol++; }
// проверим ключевые слова if (result == "repeat") { lookahead.type = tt_repeat; return; } // у нас только одно, больше проверять нечего
// если не ключевое слово, значит, идентификатор lookahead.type = tt_ident; lookahead.string_value = result; return; }
// константы if (isdigit(c)) // numeric { // отделим её от потока текста string result; // может содержать и буквы в нашей грамматике while (curridx < endidx && isalnum(text[curridx])) { result += text[curridx]; curridx++; currcol++; } auto c_str = result.c_str(); char* last_char; // есть ли способ попроще узнать, является ли строка числом без "хвоста"? long converted = strtol(c_str, &last_char, 10); // разобрали все цифры? тогда это число if (*last_char == 0) { lookahead.type = tt_number; lookahead.num_value = converted; return; } // в конце s? тогда это продолжительность if (*last_char == 's' && *(last_char + 1) == 0) { lookahead.type = tt_duration; lookahead.num_value = converted; return; } }
// ничего не нашли? окей, запомним, что это ошибка lookahead.type = tt_error; }
Продолжаем. Поскольку наш язык простой, мы не будем различать syntax tree и parse tree (то есть, синтаксическое дерево и дерево разбора), и вместо честной реализации visitor'а в syntax tree просто положим виртуальную функцию execute. (Да, я ленюсь.)
Определим классы для syntax tree. Они прямо соответствуют грамматике. (Поскольку это определения, нужно бы везде сослаться на namespace std, но я не буду утяжелять код.)
struct statement { virtual void execute() = 0; virtual ~statement() { } };
struct function_call : public statement { shared_ptr p_function; unique_ptr p_args;
virtual void execute() { p_function->function(p_args->args); } };
struct compound_statement : public statement { vector> statements; virtual void execute() { for (auto& p_statement : statements) p_statement->execute(); } };
struct repeat_statement : public statement { unique_ptr p_statement; long num_repeat; virtual void execute() { for (long i = 0; i < num_repeat; i++) p_statement->execute(); } };
struct program : public compound_statement { };
Отдельно, нам нужны структуры данных для вызовов функций. Пускай аргументы будут типизированными, время union'ов прошло.
struct arg { virtual ~arg() { } };
struct number_constant : arg { int value; };
struct duration_constant : arg { std::chrono::seconds value; };
struct arglist { vector> args; };
Можно, конечно, было бы не усложнять себе жизнь, и просто объявить имена функций ключевыми словами, но это не позволит легко добавлять новые функции.
struct installed_function { std::function>&)> function; int argnum; };
Перейдём к собственно парсеру. Он достаточно прямолинеен. Единственная хитрость — функции типа try_ пытаются найти в текущей точке потока синтаксическую конструкцию, определяя её по первому токену (вспомним, что наша грамматика LL(1)), и возвращают nullptr, если первый токен не подходит:
class parser { arg* try_parse_arg(); arglist* try_parse_arglist_until_rparen(); function_call* try_parse_function_call(); repeat_statement* try_parse_repeat_statement(); statement* try_parse_statement(); compound_statement* try_parse_compound_statement();
public: program* parse();
tokenizer tokenizer; installed_functions& functions;
public: parser(string input, installed_functions& functions) : tokenizer(input), functions(functions) { } };
Реализация функций прямо соответствует грамматике. Мы сообщаем об ошибках при помощи исключений, поэтому нам придётся пользоваться «умными» указателями, чтобы не было утечек памяти.
// program ::= statement* EOF program* parser::parse() { unique_ptr p(new program()); while (true) { // пробуем найти statement statement* s = try_parse_statement(); if (!s) // не нашли? на выход! break; p->statements.emplace_back(s); // добавляем в список }
// проверяем, что больше в файле ничего нет token t = tokenizer.peek_next(); if (t.type != tt_eof) throw parse_exception("extra characters after program end", t); return p.release(); }
// compound-statement ::= '{' statement* '}' compound_statement* parser::try_parse_compound_statement() { // ищем левую фигурную скобку token t = tokenizer.peek_next(); if (t.type != tt_lbrace) return nullptr; tokenizer.move_ahead();
unique_ptr p(new compound_statement()); // тут в цикле добавляем вложенные statement'ы, как и для program while (true) { statement* s = try_parse_statement(); if (!s) break; p->statements.emplace_back(s); }
// здесь должна быть закрывающая фигурная скобка t = tokenizer.peek_next(); if (t.type != tt_rbrace) throw parse_exception("expected closing brace after compound statement", t); tokenizer.move_ahead(); return p.release(); }
// statement ::= repeat-statement | function-call | compound-statement statement* parser::try_parse_statement() { statement* result = nullptr; // пропробуем найти repeat statement result = try_parse_repeat_statement(); if (result) // нашли? вот и хорошо return result;
// не нашли? пробуем найти function call result = try_parse_function_call(); if (result) // нашли вызов функции? прекрасно, задание выполнено return result;
// не нашли? может, тут compound statement? result = try_parse_compound_statement(); // нашли-не нашли, не интересно, вернём nullptr в крайнем случае. return result; }
// repeat-statement ::= "repeat" "(" number-constant ")" compound-statement repeat_statement* parser::try_parse_repeat_statement() { // проверяем, есть ли в начале repeat token t = tokenizer.peek_next(); if (t.type != tt_repeat) return nullptr; tokenizer.move_ahead();
// теперь обязательно скобка t = tokenizer.peek_next(); if (t.type != tt_lparen) throw parse_exception("opening parenthesis expected", t); tokenizer.move_ahead();
// теперь количество повторений t = tokenizer.peek_next(); if (t.type != tt_number) throw parse_exception("number expected", t); tokenizer.move_ahead();
// запомним его в переменую long num = t.num_value;
// тут должна быть закрывающая скобка t = tokenizer.peek_next(); if (t.type != tt_rparen) throw parse_exception("closing parenthesis expected", t); tokenizer.move_ahead();
// а за этим всем compound statement // ну, это мы умеем unique_ptr s(try_parse_compound_statement()); if (!s) throw parse_exception("compound statement expected after repeat", tokenizer.peek_next());
// всё нашли? конструируем ответ repeat_statement* rs = new repeat_statement(); rs->num_repeat = num; rs->p_statement = move(s); return rs; }
// function-call ::= ident "(" arglist ")" function_call* parser::try_parse_function_call() { // ищем идентификатор token ft = tokenizer.peek_next(); if (ft.type != tt_ident) return nullptr; tokenizer.move_ahead();
// и запомниаем имя функции string name = ft.string_value;
// проверим сначала общий синтаксис, а потом будем выяснять, // есть ли такая функция token t = tokenizer.peek_next(); if (t.type != tt_lparen) throw parse_exception("left parenthesis expected for function call", t); tokenizer.move_ahead();
unique_ptr args(try_parse_arglist_until_rparen()); if (!args) throw parse_exception("argument list not found", tokenizer.peek_next());
t = tokenizer.peek_next(); if (t.type != tt_rparen) throw parse_exception("right parenthesis expected after function arg list", t); tokenizer.move_ahead();
// обычно проверка семантики (является ли идентификатор функцией) - // задача семантического анализа, то есть, последующих этапов компиляции // но поскольку мы пишем игрушечный парсер, мы сделаем это прямо здесь auto pfunc = functions.get(name); if (!pfunc) throw parse_exception("unknown function", ft); // проверим заодно и количество аргументов if (pfunc->argnum != args->args.size()) throw parse_exception("argument count for function doesn't match", ft); // в принципе, ничего, кроме лени, не мешает нам проверить тут и *типы* аргументов // а так придётся проверять типы аргументов во время выполнения // и бросать runtime_exception
function_call* fc = new function_call(); fc->p_function = pfunc; fc->p_args = move(args); return fc; }
// arg ::= number-constant | duration-constant arg* parser::try_parse_arg() { arg* result = nullptr; token t = tokenizer.peek_next(); if (t.type == tt_number) { auto r = new number_constant(); r->value = t.num_value; result = r; } else if (t.type == tt_duration) { auto r = new duration_constant(); r->value = chrono::seconds(t.num_value); result = r; } if (result != nullptr) tokenizer.move_ahead(); return result; }
// arglist ::= EMPTY | arg ["," arg]* arglist* parser::try_parse_arglist_until_rparen() { unique_ptr result(new arglist()); token t = tokenizer.peek_next(); if (t.type == tt_rparen) return result.release();
while (true) { arg* p = try_parse_arg(); if (!p) throw parse_exception("expected argument", tokenizer.peek_next()); result->args.emplace_back(p);
t = tokenizer.peek_next(); if (t.type == tt_rparen) return result.release(); if (t.type != tt_comma) throw parse_exception("comma expected between arguments", t); tokenizer.move_ahead(); } }
Отлично, мы прошли бóльшую часть пути. Осталось лишь реализовать механизм добавления функций.
class installed_functions { unordered_map> content;
public: void install_function( function>&)> f, int argnum, string name) { installed_function* pf = new installed_function; pf->function = f; pf->argnum = argnum; content.insert(make_pair(name, shared_ptr(pf))); }
shared_ptr get(string name) { auto pfunc = content.find(name); if (pfunc == content.end()) return nullptr; return pfunc->second; } };
Определим сами функции:
void f_pause(const vector>& args) { auto parg = dynamic_cast(args[0].get()); if (!parg) throw runtime_exception("argument type mismatch in function pause"); auto duration = parg->value; cout << "pause: " << duration.count() << " seconds" << endl; }
void f_click(const vector>& args) { auto parg1 = dynamic_cast(args[0].get()); auto parg2 = dynamic_cast(args[1].get()); if (!parg1 || !parg2) throw runtime_exception("argument type mismatch in function click"); auto x = parg1->value; auto y = parg2->value; cout << "click: (" << x << ", " << y << ")" << endl; }
И можно тестировать:
string text = "repeat (8)
" "{
" "click(10,10)
" "pause(100s)
" "click(10,10)
" "repeat(2)
" " {
" " pause(10)
" " }
" "}";
int main(int argc, char* argv[]) { installed_functions functions; functions.install_function(f_pause, 1, "pause"); functions.install_function(f_click, 2, "click");
parser p(text, functions); unique_ptr tree; try { tree.reset(p.parse()); cout << "executing:" << endl; tree->execute(); } catch (const parse_exception& ex) { cerr << "parse exception at line " << ex.row << ", char " << ex.col << ": " << ex.text << endl; } catch (const runtime_exception& ex) { cerr << "runtime exception: " << ex.text << endl; }
return 0; }
Получаем вывод:
executing: click: (10, 10) pause: 100 seconds click: (10, 10) runtime exception: argument type mismatch in function pause
Ах, да, мы же неправильно указали тип аргумента для pause! Меняем pause(10) на pause(10s), всё работает как часы.

Обновление: Чуть более продвинутая версия того же парсера доступна на гитхабе

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

Помогите разобраться с наследованием и виртуальными функциями. Есть такой класс: .h -
#ifndef CUSTOMSUBWINDOW_H #define CUSTOMSUBWINDOW_H
#include
class CustomSubWindow : public QMdiSubWindow { public: explicit CustomSubWindow(QWidget *parent = 0); virtual void createUI(); };
#endif // CUSTOMSUBWINDOW_H
.cpp -
#include "CustomSubWindow.h"
CustomSubWindow::CustomSubWindow(QWidget *parent) :QMdiSubWindow(parent) { createUI(); } void CustomSubWindow::createUI() { }
Есть класс-наследник: .h -
#ifndef CAT_PRODUCTS_H #define CAT_PRODUCTS_H
#include
class CatProducts : public CustomSubWindow { Q_OBJECT public: explicit CatProducts(QWidget *parent = 0); void createUI(); };
#endif // CAT_PRODUCTS_H
.срр-
#include "CatProducts.h"
CatProducts::CatProducts(QWidget *parent) : CustomSubWindow(parent) { }
void CatProducts::createUI() { setWindowTitle("Справочник товаров!!!"); }
Т.е. в наследнике мы переопределяем метод createUI(), который вызывается в конструкторе базового класса. Как я понимаю, нужно использовать позднее связывание, т.е. объявить метод виртуальным. Но это не работает. В чём я не прав?


Ответ

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