
2 семестр / Литература / Язык программирования С++. Краткий курс. Страуструп
.pdf
220
Глава
12.
Алгоритмы
12.6.
Обзор
аnrоритмов
Общее определение алгоритма - |
"набор конечного числа |
правWI, |
ющих последовательность выполнения операций для решения |
задачи |
зада опре
деленного типа Определенность
".
...
[и] и.1wеет пять |
важных особенностей: Конечность ." |
Ввод ". Вывод ... |
Эффективность" [31, §1.1 ]. В контексте |
стандартной
библиотеки
С++
алгоритм
является
шаблоном
функции,
работа
ющим с последовательностями элементов.
Стандартная библиотека предоставляет
десятки
алгоритмов.
Алгоритмы
определены
в
пространстве
имен
s
td
и
представлены
в
заголовочном
фай
ле
<algori
thm>.
Эти
алгоритмы
стандартной
библиотеки
принимают
в
ка
честве
входных
данных
последовательности.
Полуоткрытая
последователь
ность
от
Ь до
е
записывается
как
[Ь:е).
Вот
несколько
примеров.
Избранные
стандартные
алгоритмы
f=for_each(b,e,f) |
|
p=find(b,e,x) |
|
p=find_if(b,e,f) |
|
n=count(b,e,x) |
|
n=count_if(b,e,f) |
|
replace(b,e,v,v2) |
|
replace_if(b,e,f,v2) |
|
p=copy(b,e,out) |
|
p=copy_if(b,e,out,f) |
|
p=move(b,e,out) |
|
p=unique_copy(b,e,out) |
|
sort (Ь, е) |
|
sort (Ь, е, f) |
|
(pl, р2) =equal range (Ь, |
е, v) |
p=merge (Ь, е, Ь2, е2, out) |
|
p=merge (Ь, е, Ь2, е2, out, f) |
Для каждого элементах в [Ь:е) выполнить f (х) |
|
|
р - |
первый элемент в [Ь:е), такой, что *р==х |
|
р - |
первый элемент в [Ь:е), такой, что f (*р) |
|
п - |
количество элементов *q в [Ь:е), таких, что *q==x |
|
п - |
количество элементов *q в [Ь:е), таких, что f |
(*q) |
Замена элементов *q в |
[Ь:е), таких, что *q==v, на v2 |
||
Замена элементов *q в |
[Ь:е), таких, что f (*q), на v2 |
||
Копирование |
[Ь:е) в [out:p) |
|
|
Копирование элементов *q |
из [Ь:е), таких, что f (*q), |
||
в [out:p) |
|
|
|
Перемещение |
[Ь:е) в [out:p) |
|
|
Копирование |
[Ь:е) в [out:p); |
соседние дубликаты не |
|
копируются |
|
|
|
Сортировка элементов |
[Ь:е) |
с использованием < |
|
в качестве критерия сортировки |
|||
Сортировка элементов |
[Ь:е) |
с испольэованием f |
|
в качестве критерия сортировки |
|||
[pl:p2) является подпоследовательностью отсортиро |
|||
ванной последовательности [Ь:е) со значением v; по |
|||
сути, бинарный поиск v |
|
|
|
Слияние двух отсортированных последовательностей, |
|||
[Ь:е) и [Ь2:е2), |
в [out:p) |
|
|
Слияние двух отсортированных последовательностей, |
|||
[Ь:е) и [Ь2:е2), |
в [out:p) |
с использованием f для |
сравнения

12.7.
Концепты
(С++20)
221
Эти
и
многие
другие
алгоритмы
(например,
§
14.3)
могут
применяться
к
элементам
контейнеров,
строк
и
встроенных
массивов.
Некоторые
алгоритмы,
такие
как
replace
()
и
sort
(),
изменяют
значе
ния элементов,
но
ни
один
алгоритм
не
добавляет
или
не
удаляет
элементы
контейнера.
Причина
в
том,
что
последовательность
не
идентифицирует
кон
тейнер,
который
содержит
ее
элементы.
Чтобы
добавить
или
удалить
элемен
ты,
вам
нужно
что-то,
знающее
о
конкретном
контейнере
(например,
back
_
inserter;
§12.1),
или
непосредственное
обращение
к контейнеру
(например,
push_back () или erase (); §11.2).
Для операций, передаваемых в качестве
ются лямбда-выражения. Например:
аргументов,
очень
часто
применя
vector<int> v = {0,1,2,3,4,5);
for_each(v.begin() ,v.end(), [] (int&x)
{х=х*х;
})
;
11
v=={0,1,4,9,16,25}
Алгоритмы
стандартной
библиотеки,
как
правило,
более
тщательно
разра
ботаны,
специфицированы
и
реализованы,
чем
средний
созданный
вручную
цикл, поэтому |
их нужно |
му на "голом" |
языке. |
знать и
использовать,
предпочитая
коду,
написанно
12.7.
Концепты
(С++20)
В
конце
концов
алгоритмы
стандартной
библиотеки
будут
определены
с
использованием
концептов
(глава
7,
"Концепты
и
обобщенное
программиро
вание").
Предварительные
обсуждения
этого
вопроса
можно
найти
в
Ranges
Technical Specification [37],
а
реализации
можно
найти
в
Интернете.
Концеп
ты
определены
в
<experimental/Ranges>,
но,
надеюсь,
что-то
очень
похо
жее
будет
добавлено
в
пространство
имен
std
в
С++20.
Диапазоны
Range
представляют
собой
обобщение
последовательностей
С++98,
определяемых
парами
begin
()
/end ().
Диапазон
-
это
понятие,
определяющее,
что
может
представлять
собой
последовательность
элемен
тов.
Его
можно
определить
как
• •
•
пару |
итераторов {beg in, end}; |
|
|
|
|
|
|||
пару |
{beg in, n}, где beg in |
представляет собой итератор, а n - |
коли |
||||||
чество элементов; |
|
|
|
|
|
|
|
||
пару |
{beg in, pred}, |
где |
beg in |
представляет собой |
итератор, |
а |
|||
pred- |
предикат; если pred (р) возвращает true для итератора р, мы |
||||||||
достигли |
конца последовательности. |
Это позволяет |
нам |
иметь |
беско |
||||
нечные последовательности |
и последовательности, |
генерируемые "на |
|||||||
лету". |
|
|
|
|
|
|
|
|


12.7.
Концепты
(С++20)
223
шанными
типами,
для
которых
библиотека
(пока
еще)
не
имеет
подходящих
определений. |
Cornrnon или |
большинства |
концептов и |
CornrnonReference используется в определениях
алгоритмов, которые могут сравнивать значения
разных типов. На концепты,
связанные
со
сравнением,
сильно
повлияла
книга
[40].
Концепты
сравнений
Boolean<T>
WeaklyEqualityComparaЬleWith<T, U>
т может использоваться как булева величина
т и u моrут сравниваться на равенство с использованием операторов == и ! =
WeaklyEqualityComparaЬle<T>
WeaklyEqualityComparaЬleWith<T,T>
Equali tyComparaЬleWith<T, U>
EqualityComparaЬle<T>
т и u моrут сравниваться на равенство с
использованием оператора == EqualityComparaЬleWith<T,T>
StrictTotallyOrderedWi th<T, U>
StrictTotallyOrdered<T>
т и u моrут сравниваться с использованием
операторов <, <=, > и >=,дающих полное упорядочение StrictTotallyOrderedWith<T,T>
Применение
как
WeaklyEquali
tyComparaЬleWi
th,
так
и
WeaklyEqua
li
tyComparaЫe
демонстрирует
отсутствие
(до
настоящего
времени)
воз
можности
перегрузки.
Концепты,
связанные
с
объектами
DestructiЫe<T>
ConstructiЫe<T, Args>
Defaul tConstructiЫe<T>
MoveConstructiЫe<T>
CopyConstructiЬle<T>
МоvаЫе<Т>
т может быть уничтожен, а его адрес может быть |
|
получен с помощью унарного оператора & |
|
т может быть построен из списка арrументов типа Args |
|
т может быть создан с помощью конструктора |
по |
умолчанию |
|
т может быть создан с помощью перемещающего |
|
конструктора |
|
т может быть создан с помощью копирующего |
|
и перемещающего конструкторов |
|
MoveConstructaЬle<T>,AssignaЫe<T&,T> |
|
и SwapaЬle<T> |
|
СоруаЫе<Т>
Semiregular<T>
Regular<T>
CopyConstructaЫe<T>,MoveaЬle<T>
и AssignaЬle<T, const Т&>
СоруаЫе<Т> и Defaul tConstructaЬle<T>
SemiRegular<T> и Equali tyComparaЬle<T>

224
Глава
12.
Алгоритмы
Regular
-
идеал
для
типов.
Тип,
соответствующий
концепту
Regular,
грубо
говоря,
работает
как
in t
и
упрощает
большую
часть
наших
размыш
лений о том, оператора ==
как |
использовать этот |
тип |
(§7.2). Отсутствие по |
умолчанию |
для |
классов означает, |
что |
большинство классов |
относятся к |
SemiRegular,
хотя
большинство
из
них
могут
и
должны
быть
Regular.
Концепты,
связанные
с
вызовами
InvocaЬle<F, Args>
InvocaЬleRegular<F, Args>
Predicate<F, Args>
F может быть вызван со списком аргументов типа Args
InvocaЬle<F, Args> и сохраняет равенство
F может быть вызван со списком аргументов типа Args
и возвращает bool
Relation<F,T,U>
StrictWeakOrder<F, т, U>
Predicate<F,T,U> |
|
Relation<F, т, U>, |
предоставляющий строгое слабое |
упорядочение |
|
Функция
f |
( ) |
называется
сохраняющей
равенство
(equality
preserving),
если
из
х==у следует, что f (х) ==f (у).
Строгое слабое упорядочение (strict weak ordering) -
это
то,
что
стандарт
ная
библиотека
обычно
предполагает
для
сравнений,
таких
как
<;
поищите
соответствующую
информацию
в
учебниках
или
Интернете,
если
вас
заинте
ресовал этот вопрос.
Relation и StrictWeakOrder
различаются
только
семантикой.
Мы
не
можем
(в
настоящее
время)
представить
это
различие
в
коде,
поэтому
просто
выражаем
наши
намерения
с
помощью
имен.
Концепты,
связанные
с
итераторами
Iterator<I>
Sentinel<S,I>
SizedSentinel<S,I>
Inputlterator<I>
Outputlterator<I>
Forwardlterator<I>
Bidirectionallterator<I>
К I |
могут быть применены операторы инкремента |
(++) |
||
и разыменования(*) |
|
|
|
|
s является ограничителем для типа итератора, т.е. s |
||||
является предикатом над значением типа I |
|
|
||
Ограничитель s, где к I |
может быть применен |
|
|
|
оператор- |
|
|
|
|
I - |
входной итератор; |
оператор * может быть |
|
|
применен только для чтения |
|
|
||
I - |
выходной итератор; оператор * может быть |
|
|
|
применен только для записи |
|
|
||
I - |
однонаправленный итератор, поддерживающий |
|||
многопроходность |
|
|
|
|
I - |
Forwardlterator |
с поддержкой оператора |
-- |
|

12.7.
Концепты
(С++20)
225
Окончание
табл.
Концепты,
связанные
с итераторами
RandomAccessiterator<I>
PermutaЫe<I>
MergeaЬle<Il,I2,R,O>
SortaЫe<I>
SortaЫe<I,R>
I -Bidirectionaliterator с подцержкой
операторов+,-,+=,-= и [J
I - Forwarditerator<I>, где I разрешает
перемещать и обменивать элементы
Можно сливать отсортированные последовательности,
определяемые I 1 и I2, в о с использованием
Relation<R>
Можно сортировать последовательности, определяе
мые I, с использованием отношения меньше
Можно сортировать последовательности,
определяемые I, с использованием Relation<R>
ра
Различные |
разновидности (категории) итераторов используются для |
выбо |
наилучшей |
реализации для данного алгоритма; см. §7.2.2 и §13.9.1. |
При |
мер Inputiterator см. в §12.4.
Основная идея ограничителя
состоит
в
том,
что
мы
можем
перебирать
диапазон,
начиная
с
определенного
итератора,
пока
для
элемента
не
станет
истинным
указанный
предикат.
Таким
образом,
итератор
р
и
ограничитель
s
определяют
диапазон
[p:s
(
*р)
).
Например,
мы
могли
бы
определить
преди
кат
для ограничения
для
обхода
строки
в
стиле
С,
используя
в
качестве
итера
тора
указатель:
[]
(const char*
р)
{return
*р==О;
)
Изложение информации щено по сравнению с [37].
о
MergeaЫe
и
SortaЫe
в
данной
книге
упро
Range<R>
SizedRange<R>
View<R>
BoundedRange<R>
InputRange<R>
Концепты диапазонов
Rпредставляет собой диапазон с начальным итератором
и ограничителем
Rпредставляет собой диапазон с получением размера за
константное время
Rпредставляет собой диапазон с копированием,
перемещением и прием за константное время
Rпредставляет собой диапазон с идентичными типами
итератора и ограничителя
Rпредставляет собой диапазон, тип итератора которого
удовлетворяет концепту Inputiterator


12.9.
Параллельные
алгоритмы
227
12.9.
Параллельные
алгоритмы
Если
одна
и та же
задача
должна
быть
выполнена
для
многих
элементов
данных, можно выполнять ее параллельно для каждого элемента данных условии, что вычисления для различных элементов данных независимы:
при
•
параллельное выполнение: задания выполняются несколькими потоками
выполнения (зачастую работающими на разных ядрах процессора);
•
векторизованное выполнение: задания |
выполняются |
на одном потоке с |
|
использованием |
векторизации, известной как SIMD |
("Single Instruction, |
|
Multiple Data" - |
одна команда, много |
данных). |
|
Стандартная
библиотека
предлагает
поддержку
для
обоих
вариантов,
и
мы
можем
точно
указать
требование
последовательного
выполнения;
в
заголо
вочном
файле
<execution>
имеются
следующие
индикаторы
стратегий:
• • •
seq: последовательное выполнение; |
|
par: параллельное выполнение (если таковое возможно); |
|
par_ unseq: параллельное и/или непоследовательное |
(векторизован |
ное) выполнение (если таковое возможно). |
|
Рассмотрим
std::
sort
():
sort (v .begin (), v. end ()); |
/ / Последовательное |
sort(seq,v.begin(),v.end()); |
//Последовательное |
sort(par,v.begin(),v.end()); |
//Параллельное |
11 Параллельное и/или векторизованное: |
|
sort(par_unseq,v.begin(),v.end()); |
(как
и
по умолчанию)
Стоит
ли
распараллеливать
и/или
векторизовать
выполнение,
зависит
от
алгоритма,
количества
элементов
в
последовательности,
аппаратного
обеспе
чения
и
использования
этого
аппаратного
обеспечения
программами, работа
ющими
на
нем.
Следовательно,
индикаторы
стратегии
выполнения
являют
ся
просто
подсказками.
Компилятор
и/или
планировщик
времени
выполнения
решат,
какой
параллелизм
использовать.
Это
все
очень
нетривиальные
задачи,
и очень важно не делать никаких утверждений об эффективности параллельности без проведения детальных измерений.
применения
Для
большинства
алгоритмов
стандартной
библиотеки,
включая
все
табли
цы
в
§12.6,
за
исключением
equal
Range,
может
быть
запрошено
распарал
леливание
и
векторизация
с
использованием
par
и
par
_
unseq,
как
в
случае
sort
()
.
Почему
не
equal
_
Range
()?Потому
что
до
сих
пор
никто
не
приду
мал для него достойного параллельного алгоритма. |
|
Многие параллельные алгоритмы |
используются |
данных; см. §14.3.1. |
|
в
основном
для
числовых

228
Глава
12.
Алгоритмы
При запросе параллельного выполнения следует убедиться
возможности гонки данных(§15.2) и взаимоблокировки(§15.5).
в
отсутствии
12.10.
Советы
[1]
[2]
[3]
[4]
[5]
[6]
[7] [8]
[9]
Алгоритмы STL работают с |
одной или несколькими последовательно |
||||||
стями; §12.1. |
|
|
|
|
|
|
|
Входная последовательность |
является |
полуоткрытой и определяется |
па |
||||
рой итераторов; §12.1. |
|
|
|
|
|
|
|
Алгоритм поиска обычно возвращает |
конец входной последовательно |
||||||
сти как указатель неудачности выполненного поиска; §12.2. |
|
|
|
||||
Алгоритмы непосредственно |
не добавляют и не убирают элементы |
из |
|||||
последовательностей, |
переданных им в качестве аргументов; |
§12.2, |
|||||
§12.6. |
|
|
|
|
|
|
|
При |
написании цикла |
подумайте, не может ли он быть выражен |
как |
об |
|||
щий |
алгоритм; §12.2. |
|
|
|
|
|
|
Используйте предикаты и другие функциональные объекты |
для при |
||||||
дания стандартным алгоритмам более широкого диапазона |
смыслов; |
||||||
§12.5, §12.6. |
|
|
|
|
|
|
|
Предикат не должен модифицировать |
свой аргумент; §12.5. |
|
|
|
|||
Следует знать алгоритмы стандартной библиотеки и предпочитать |
их |
||||||
написанным вручную |
циклам; §12.6. |
|
|
|
|
||
Если |
написание пары |
итераторов становится утомляющим, вводите |
ал |
||||
горитмы для контейнеров/диапазонов; §12.8. |
|
|
|

13 Утилиты
Время,
которое вы тратите
с удовольствием,
не
тратится
впустую.
-
+ |
Введение |
|
+ |
Управление ресурсами |
|
|
unique_Ptr И shared_Ptr |
|
+ |
move() |
И forward() |
Проверка |
выхода эа границы диапаэона: gsl::spaп |
|
+ |
Специалиэированные контейнеры |
|
|
array |
|
|
Ьitset |
|
+ |
pair И tuple |
|
Альтернативы |
||
|
variant |
|
optional |
|
+ |
any |
|
Аллокаторы |
||
+ |
Время |
|
+ |
Адаптация |
функций |
|
Лямбда-выражения |
в
качестве
адаптеров
|
mem_fn () |
|
+ |
function |
|
Функции типов |
||
|
iterator |
traits |
|
Предикаты типов |
|
|
еnаЫе if |
|
Бертран
Рассел
+
Советы