diff --git a/1-js/4-data-structures/11-datetime/article.md b/1-js/4-data-structures/11-datetime/article.md index ec7e1af9..5cd1fa55 100644 --- a/1-js/4-data-structures/11-datetime/article.md +++ b/1-js/4-data-structures/11-datetime/article.md @@ -281,7 +281,7 @@ alert( 'Время walkLength: ' + bench(walkLength) + 'мс' ); Теперь представим себе, что во время первого бенчмаркинга `bench(walkIn)` компьютер что-то делал параллельно важное (вдруг) и это занимало ресурсы, а во время второго -- перестал. Реальная ситуация? Конечно реальна, особенно на современных ОС, где много процессов одновременно. -**Гораздо более надёжные результаты можно получить, весь пакет тестов прогнать много раз.** +**Гораздо более надёжные результаты можно получить, если весь пакет тестов прогнать много раз.** ```js //+ run diff --git a/1-js/4-data-structures/12-typeof-duck-typing/article.md b/1-js/4-data-structures/12-typeof-duck-typing/article.md index 7c36628c..733bf476 100644 --- a/1-js/4-data-structures/12-typeof-duck-typing/article.md +++ b/1-js/4-data-structures/12-typeof-duck-typing/article.md @@ -121,7 +121,7 @@ if (x.getTime) { ## Полиморфизм -Пример полимофрной функции -- `sayHi(who)`, которая будет говорить "Привет" своему аргументу, причём если передан массив -- то "Привет" каждому: +Пример полиморфной функции -- `sayHi(who)`, которая будет говорить "Привет" своему аргументу, причём если передан массив -- то "Привет" каждому: ```js //+ run diff --git a/1-js/4-data-structures/5-object-for-in/article.md b/1-js/4-data-structures/5-object-for-in/article.md index 51828370..8418af31 100644 --- a/1-js/4-data-structures/5-object-for-in/article.md +++ b/1-js/4-data-structures/5-object-for-in/article.md @@ -166,6 +166,6 @@ for (var code in codes) {
'w'
вполне подходит:
+В данном случае следующий символ шаблона: .
(точка). Она обозначает "любой символ", так что следующая буква строки 'w'
вполне подходит:
.+
. Движок регулярных выражений берёт один символ за другим, до тех пор, пока у него это получается.
@@ -63,32 +63,40 @@ alert( str.match(reg) ); // "witch" and her "broom"
В данном случае это означает "до конца строки":
.+
и очень рад по этому поводу.
+.+
и переходит к следующему символу шаблона.
Следующий символ шаблона -- это кавычка. Её тоже необходимо найти, чтобы соответствие было полным. А тут -- беда, ведь поисковый текст завершился!
-Движок регулярных выражений понимает, что, наверное, взял многовато .+
и начинает отступать обратно ("фаза бэктрекинга" -- backtracking на англ.).
+Движок регулярных выражений понимает, что, наверное, взял многовато .+
и начинает отступать обратно.
Иными словами, он сокращает текущее совпадение на один символ:
-'"'
не совпадает с 'e'
..+
ещё раз:
+Это называется "фаза возврата" или "фаза бэктрекинга" (backtracking -- англ.).
-.+
соответствует почти вся оставшаяся строка, за исключением одного символа, и движок регулярных выражений ещё раз пытается подобрать соответствие для остатка шаблона, начиная с оставшейся части строки.
+
+Если бы последним символом строки была кавычка '"'
, то на этом бы всё и закончилось. Но последний символ 'e'
, так что совпадения нет..+
ещё на один символ:
+
+'"'
не совпадает с 'n'
. Опять неудача.'.'
до тех пор, пока остаток паттерна не совпадёт:
+'.'
до тех пор, пока остаток паттерна, то есть в данном случае кавычка '"'
, не совпадёт:
-is one
новых совпадений не даст..+
повторился максимальное количество раз, что и привело к такой длинной строке.
+
+А мы, наверное, хотели, чтобы каждая строка в кавычках была независимым совпадением? Для этого можно переключить квантификатор `+` в "ленивый" режим, о котором будет речь далее.
## Ленивый режим
@@ -109,40 +117,38 @@ var str = 'a "witch" and her "broom" is one';
alert( str.match(reg) ); // witch, broom
```
-Чтобы в точности понять, что происходим, разберём в деталях, как ищется ".+?"
.
+Чтобы в точности понять, как поменялась работа квантификатора, разберём поиск по шагам.
'"'
найдена на 3й позиции:
-'.'
:
-'"'
:
+'"'
:
-+?
. Квантификаторы +?
и ??
ведут себя аналогично -- "ленивый" движок увеличивает количество повторений только в том случае, если для остальной части шаблона на данной позиции нет соответствия, в то время как жадный сначала берёт столько повторений, сколько возможно, а потом отступает назад.
+В примере выше продемонстрирована работа ленивого режима для +?
. Квантификаторы +?
и ??
ведут себя аналогично -- "ленивый" движок увеличивает количество повторений только в том случае, если для остальной части шаблона на данной позиции нет соответствия.
**Ленивость распространяется только на тот квантификатор, после которого стоит `?`.**
@@ -156,14 +162,17 @@ alert( "123 456".match(/\d+ \d+?/g) ); // 123 4
```
\d+
пытается найти столько символов, сколько возможно (работает жадно), так что он находит 123
и останавливается, поскольку символ пробела ' '
не подходит под \d
.\d+?
.
+\d+
пытается найти столько цифр, сколько возможно (работает жадно), так что он находит 123
и останавливается, поскольку символ пробела ' '
не подходит под \d
.\d+?
.
-Он находит один символ '4'
и пытатся проверить, есть ли совпадение с остатком шаблона (после \d+?
).
+Квантификатор указан в ленивом режиме, поэтому он находит одну цифру 4
и пытается проверить, есть ли совпадение с остатком шаблона.
-Здесь мы ещё раз заметим -- ленивый режим без необходимости ничего не возьмёт.
+Но после \d+?
в шаблоне ничего нет.
-Так как шаблон закончился, то поиск завершается и 123 4
становится результатом.123 4
.
"[^"]+"
даст правильные результаты, поскольку ищет кавычку '"'
, за которой идут столько не-кавычек (исключающие квадратные скобки), сколько возможно. Так что вторая кавычка автоматически прекращает повторения [^"]+
и позволяет найти остаток шаблона "
.
+Регэксп "[^"]+"
даст правильные результаты, поскольку ищет кавычку '"'
, за которой идут столько не-кавычек (исключающие квадратные скобки), сколько возможно.
+
+Так что вторая кавычка автоматически прекращает повторения [^"]+
и позволяет найти остаток шаблона "
.
+
+**Эта логика ни в коей мере не заменяет ленивые квантификаторы!**
+
+
+Она просто другая. И то и другое бывает полезно.
+
+Давайте посмотрим пример, когда нужен именно такой вариант, а ленивые квантификаторы не подойдут.
+
+Например, мы хотим найти в тексте ссылки вида ``, с любым содержанием `href`.
+
+Какое регулярное выражение для этого подойдёт?
+
+Первый вариант может выглядеть так: /<a href=".*" class="doc">/g
.
+
+Проверим его:
+```js
+//+ run
+var str = '......';
+var reg = //g;
+
+// Сработало!
+alert( str.match(reg) ); //
+```
+
+А если в тексте несколько ссылок?
+
+```js
+//+ run
+var str = '...... ...';
+var reg = //g;
+
+// Упс! Сразу две ссылки!
+alert( str.match(reg) ); // ...
+```
+
+На этот раз результат неверен.
+
+Жадный .*
взял слишком много символов.
+
+Соответствие получилось таким:
+```
+
+...
+```
+
+Модифицируем шаблон -- добавим ленивость квантификатору .*?
:
+
+```js
+//+ run
+var str = '...... ...';
+var reg = //g;
+
+// Сработало!
+alert( str.match(reg) ); // ,
+```
+
+Теперь всё верно, два результата:
+
+```
+
+...
+```
+
+Почему теперь всё в порядке -- для внимательного читателя, после объяснений, данных выше в этой главе, должно быть полностью очевидно.
+
+Поэтому не будем останавливаться здесь на деталях, а попробуем ещё пример:
+
+```js
+//+ run
+var str = '...... ...';
+var reg = //g;
+
+// Неправильное совпадение!
+alert( str.match(reg) ); // ...
+```
+
+Совпадение -- не ссылка, а более длинный текст.
+
+Получилось следующее:
+ `, вообще не имеющем отношения к ``.
+
+```
+ ...';
+var str2 = '...... ...';
+var reg = //g;
+
+// Работает!
+alert( str1.match(reg) ); // null, совпадений нет, и это верно
+alert( str2.match(reg) ); // ,
+```
+
+## Итого
+
+Квантификаторы имеют два режима работы:
+
+
+
+Итак, ленивость нам не помогла.
+
+Необходимо как-то прекратить поиск <a href="
..*?
, после каждого символа проверяя, есть ли совпадение остальной части шаблона.
+
+Подшаблон .*?
будет брать символы до тех пор, пока не найдёт class="doc">
.
+
+В данном случае этот поиск закончится уже за пределами ссылки, в теге `.*
, чтобы он не вышел за пределы кавычек.
+
+Для этого мы используем более точное указание, какие символы нам подходят, а какие нет.
+
+Правильный вариант: [^"]*
. Этот шаблон будет брать все символы до ближайшей кавычки, как раз то, что требуется.
+
+Рабочий пример:
+
+```js
+//+ run
+var str1 = '......
+
+
+Как мы видели в примере выше, ленивый режим -- не панацея от "слишком жадного" забора символов. Альтернатива -- более аккуратно настроенный "жадный", с исключением символов. Как мы увидим далее, можно исключать не только символы, но и целые подшаблоны.
+
+
diff --git a/10-regular-expressions-javascript/8-regexp-greedy-and-lazy/witch_greedy1.svg b/10-regular-expressions-javascript/8-regexp-greedy-and-lazy/witch_greedy1.svg
index 9db83ff6..28a19210 100644
--- a/10-regular-expressions-javascript/8-regexp-greedy-and-lazy/witch_greedy1.svg
+++ b/10-regular-expressions-javascript/8-regexp-greedy-and-lazy/witch_greedy1.svg
@@ -1,5 +1,5 @@
-