On-line: гостей 0. Всего: 0 [подробнее..]
Программисты всех стран, объединяйтесь!

АвторСообщение



ссылка на сообщение  Отправлено: 10.10.18 17:04. Заголовок: Как выполнить инверсию отдельных слов в объекте класса std::string


Для начала следует определить, что является словом в объекте класса std::string. Будем считать словом набор символов, ограниченных символом пробела - ' ' и символом табуляции - '\t'.

Например, в следующем объекте s класса std::string, определенным как

 
std::string s( "one two three" );


имеется три слова: "one", "two" и "three".

Нужно изменить строку таким образом, чтобы она имела вид "eno owt eerht".

Ниже представлена демонстрационная программа, которая показывает соответствующую функцию, выполняющую требуемую задачу:

 
#include <iostream>
#include <iomanip>
#include <string>
#include <algorithm>
#include <iterator>

std::string & reverse_words( std::string &s )
{
const char *blank = " \t";

for ( std::string::size_type last = 0, first = s.find_first_not_of( blank, last );
first != std::string::npos;
first = s.find_first_not_of( blank, last ) )
{
last = s.find_first_of( blank, first );
if ( last == std::string::npos ) last = s.size();
std::reverse(std::next( std::begin( s ), first ), std::next( std::begin( s ), last ) );
}

return s;
}

int main()
{
std::string s( "one two three" );

std::cout << std::quoted( s ) << '\n';
std::cout << std::quoted( reverse_words( s ) ) << '\n';
}


Вывод программы на консоль:
 
"one two three"
"eno owt eerht"


Из результирующей строки, которую построила функция, легко получить строку вида "three two one". Для этого достаточно применить стандартный алгоритм std::reverse к результирующей строке. Например,

 
#include <iostream>
#include <iomanip>
#include <string>
#include <algorithm>
#include <iterator>

std::string & reverse_words( std::string &s )
{
const char *blank = " \t";

for ( std::string::size_type last = 0, first = s.find_first_not_of( blank, last );
first != std::string::npos;
first = s.find_first_not_of( blank, last ) )
{
last = s.find_first_of( blank, first );
if ( last == std::string::npos ) last = s.size();
std::reverse(std::next( std::begin( s ), first ), std::next( std::begin( s ), last ) );
}

return s;
}

int main()
{
std::string s( "one two three" );

std::cout << std::quoted( s ) << '\n';
std::cout << std::quoted( reverse_words( s ) ) << '\n';

std::reverse( std::begin( s ), std::end( s ) );
std::cout << std::quoted( s ) << '\n';
}


Вывод программы на консоль:
 
"one two three"
"eno owt eerht"
"three two one"


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

  
std::reverse( std::begin( s ), std::end( s ) );

начальный и конечный итераторы, которые будут указывать на первый не пробельный символ и последний пробельный символ в строке. Например,
 
#include <iostream>
#include <iomanip>
#include <string>
#include <algorithm>
#include <iterator>

std::string & reverse_words( std::string &s )
{
const char *blank = " \t";

for ( std::string::size_type last = 0, first = s.find_first_not_of( blank, last );
first != std::string::npos;
first = s.find_first_not_of( blank, last ) )
{
last = s.find_first_of( blank, first );
if ( last == std::string::npos ) last = s.size();
std::reverse(std::next( std::begin( s ), first ), std::next( std::begin( s ), last ) );
}

return s;
}

int main()
{
std::string s( "\tone\ttwo\tthree " );

std::cout << std::quoted( s ) << '\n';
std::cout << std::quoted( reverse_words( s ) ) << '\n';

const char *blank = " \t";

auto first = std::next( std::begin( s ), s.find_first_not_of( blank ) );
auto last = std::next( std::begin( s ), s.find_last_not_of( blank ) + 1 );

std::reverse( first, last );
std::cout << std::quoted( s ) << '\n';
}


Вывод программы на консоль:
 
" one two three "
" eno owt eerht "
" three two one "


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

 
#include <iostream>
#include <iomanip>
#include <string>
#include <string_view>
#include <iterator>
#include <algorithm>

std::string & reverse_words( std::string &s, const std::string_view &delimeter = " \t" )
{
for ( std::string::size_type last = 0, first = s.find_first_not_of( delimeter, last );
first != std::string::npos;
first = s.find_first_not_of( delimeter, last ) )
{
last = s.find_first_of( delimeter, first );
if ( last == std::string::npos ) last = s.size();
std::reverse(std::next( std::begin( s ), first ), std::next( std::begin( s ), last ) );
}

return s;
}

int main()
{
std::string s( "\tone\ttwo\tthree " );

std::cout << std::quoted( s ) << '\n';
std::cout << std::quoted( reverse_words( s ) ) << '\n';

const char *blank = " \t";

auto first = std::next( std::begin( s ), s.find_first_not_of( blank ) );
auto last = std::next( std::begin( s ), s.find_last_not_of( blank ) + 1 );

std::reverse( first, last );
std::cout << std::quoted( s ) << '\n';
}

Вывод программы на консоль будет таким же, как и при запуске предыдущей демонстрационной программы
 
" one two three "
" eno owt eerht "
" three two one "


Спасибо: 0 
ПрофильЦитата Ответить
Ответов - 5 [только новые]





ссылка на сообщение  Отправлено: 12.10.18 18:02. Заголовок: Данную тему я создал..


Данную тему я создал в виду обсуждения вопроса Зачем просят перевернуть строку на интервью? на форуме RSDN в разделе "О работе".

Вот это сообщение

 цитата:
и сколько времени можно на это потратить у доски?

Условие было такое: строка «один два три». Написать функцию на доске, чтобы поменять порядок символов внутри этой строки, не выделяя новой памяти (кроме временных переменных). Принимается ли решение через 15 минут?


Изначально я воспринял задание в исходном сообщении темы как требование выполнить инверсию отдельных слов в строке в программе на C++. Почему на C++? Потому что автор темы последнее время создает темы про C++ из-за своего желания перейти в своей профессии на программирование на этом языке.

Однако, как оказалось, задание на самом деле значительно проще. Нужно написать функцию, которая выполняет инверсию обычной символьной C-строки.

Сначала я покажу возможные решения этого задания.

Во-первых, сразу же отмечу, что лучше всего эту функцию писать как C-функцию, так как очень часто в проектах на C++ используется и C-код, в котором такая функция может пригодиться. То есть написав такую функцию на C вы значительно расширяете ее область применения. При этом никаких побочных нежелательных эффектов для кода на C++ эта функция иметь не будет.

Например, функцию можно реализовать следующим образом, как это показано в демонстрационной программе, написанной на C.
 
#include <stdio.h>
#include <string.h>

char * str_reverse( char *s )
{
for ( size_t i = 0, n = strlen( s ); i < n / 2; i++ )
{
char c = s[ i ];
s = s[ n - i - 1];
s[ n - i - 1 ] = c;
}

return s;
}

int main( void )
{
char s[] = "Hello World!";

puts( s );
puts( str_reverse( s ) );
}

Вывод программы на консоль:
 
Hello World!
!dlroW olleH

Отметим важные моменты реализации этой функции.

Во-первых, такой функции следует возвращать указатель на исходную строку. Это следует общему соглашению стандартных строковых C-функций.
Во-вторых, в функции не должно быть проверки на то, что переданный указатель не является равным NULL. Это также вытекает из соглашения о строковых функциях в C. Эта функция - функция низкого уровня, и она не должна делать лишних проверок. Это задача клиента - обеспечить передачу в функцию в качестве аргумента указателя не равного NULL. Если пользователь передаст указатель равный NULL, то функция будет иметь неопределенное поведение. Такая реализация функции делает общим подход к использованию строковых функций в C низкого уровня и дисциплинирует программистов. То есть поведение функции в точности соответствует поведению других стандартных строковых C-функций низкого уровня.

Как известно, оператор индексации, то есть выражение E1[E2], определяется следующим образом как выражение (*((E1)+(E2))). Поэтому вместо использования оператора индексирования в функции ее можно переписать с использованием указателей. Например,

 
#include <stdio.h>
#include <string.h>

char * str_reverse( char *s )
{
size_t n = strlen( s );

if ( !( n < 2 ) )
{
for ( char *first = s, *last = s + n; first < --last; ++first )
{
char c = *first;
*first = *last;
*last = c;
}
}

return s;
}

int main( void )
{
char s[] = "Hello World!";

puts( s );
puts( str_reverse( s ) );
}


Вывод программы будет аналогичен, показанному выше:
 
Hello World!
!dlroW olleH

А теперь рассмотрим те ошибки, которые допускали программисты в обсуждении этой темы.
Вот, к примеру в сообщении этой темы от CreatorCray предлагаются такие решения
Первое
 
#include <algorithm>

void revert(char* first, char* last)
{
while ((first != last) && (first != --last)) {
std::iter_swap (first++, last);
}
}

Сразу же заметим, что для строковых функций следует возвращать указатель на результирующую строку. как было сказано выше, потому что это, фактически, C-функция низкого уровня. Второе. Указатели являются итераторами произвольного доступа, а потому условие цикла (first != last) && (first != --last) явно перегружено. Этот фрагмент кода с циклом можно было бы переписать следующим образом
 
if ( first != last )
{
for ( ; first < --last; ++first )
{
std::iter_swap( first, last );
}
}

То есть проверка условия first != last делается только один раз перед циклом. И для большей читаемости кода лучше заменить цикл while на цикл for.

Второе решение от CreatorCray выглядит следующим образом
#include <stddef.h>

 
void revert(char* s, size_t n)
{
for (auto i=0; i<n/2; i++)
{
auto c = s;
s = s[n-1-i];
s[n-1-i] = c;
}
}

Данная функция вообще имеет неопределенное поведение, так как тип переменной i является int. А так как ранг типа int очевидно меньше ранга типа size_t, то объект типа int может в общем случае не содержать все значения объекта типа size_t. В результате, например, можно получить бесконечный цикл. То есть левый операнд в выражении i<n/2 может быть всегда меньше правого операнда.

Вот еще пример реализации от AleksandrN
 
void reverse( char *s )
{
if ( !s )
return;

char *e = s + strlen( s ) - 1;
while ( s < e )
{
*e ^= *s;
*s ^= *e;
*e-- ^= *s++;
}
}

Еще раз обращу внимание на то, что функция должна возвращать указатель на результирующую строку, а не иметь тип возврата void, а также не должна делать проверку аргумента на равенство NULL.. Кстати сказать, замечу, что даже POSIX функция strdup не делает такой проверки, хотя было бы вполне логично возвращать NULL, если функции был передан в качестве аргумента null-указатель. Но даже если игнорировать эти замечания, тем не менее функция имеет неопределенное поведение. Пользователь функции может передать в качестве аргумента пустую строку. В этом случае объявление char *e = s + strlen( s ) - 1; эквивалентно объявлению char *e = s - 1;, в результате чего значение указателя e имеет не действительное значение, так как нельзя выходить за пределы начала массива..

Подытоживая, хочу сказать следующее. Если в организации таким образом проводят интервью, то те, кто ответственен за набор новых сотрудников, является некомпетентными. На собеседование, если вам требуются квалифицированные программисты, следует приглашать тех, о квалификации которых у вас уже имеется твердое мнение. Квалифицированный программист - это публичный человек. Его можно найти, например, на специализированных форумах, например, на форуме по обсуждению C++ стандарта, или на таком форуме, как Stackoverflow, и посмотреть его сообщения, статьи, его репутацию и его ответы, чтобы достаточно полно сложилось мнение о нем, как о программисте. После этого и следует приглашать на собеседование, а не на экзамен.

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

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

Кстати сказать, в продолжение этой темы для расширения кругозора, думаю, будет интересно ознакомиться с моим предложением по изменению стандарта C++ относительно алгоритма reverse. Смотрите тему "Предложение по включению в стандарт C++ новых обобщенных функций std::reverse и std::sort" по ссылке Предложение по включению в стандарт C++ новых обобщенных функций std::reverse и std::sort

Спасибо: 0 
ПрофильЦитата Ответить



ссылка на сообщение  Отправлено: 14.10.18 20:55. Заголовок: Практически, почти о..


Практически, почти одновременно встретил аналогичный вопрос начинающего программиста относительно написания функции инверсии символьной C-строки на сайте Stackoverflow Error in checking if pointer reaches end-of-string.

Похоже, что автору вопроса требуется написать такую функцию без использования других стандартных строковых функций таких, как функция strlen.

Вот реализация функции, представленная автором вопроса в его исходном вопросе:
 
void reverse(char* p){

char* start = p;
char* end = p;
char tmp;
int length =0;


while(*end!='\0'){
end+=1;
length+=1;
}
printf("%c\n",*end); // problem line

int c;

for (c = 0; c < length/2; c++)
{
tmp = *start;
*start = *end;
*end = tmp;

start++;
end--;

}
//printf("%s\n",p);
}

Как уже неоднократно говорилось мною ранее в этой теме такой функции следует возвращать указатель на результирующую строку.

Другое замечание относительно реализации функции состоит в том. как все начинающие программисты, он использует в теле функции совершенно излишнюю переменную length для хранения длины строки.Если уже имеются в распоряжении указатели на начало и конец строки, то этого вполне достаточно для перестановки символов строки.

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

Вот как может выглядеть реализация этой функции без использования стандартной функции strlen.
 
#include <stdio.h>

char * str_reverse( char *s )
{
char *first = s, *last = s;

while ( *last ) ++last;

if ( first != last )
{
for ( ; first < --last; ++first )
{
char c = *first;
*first = *last;
*last = c;
}
}

return s;
}

int main(void )
{
char s[] = "Hello World!";

puts( s );
puts( str_reverse( s ) );
}

Вывод программы на консоль:
 
Hello World!
!dlroW olleH


Спасибо: 0 
ПрофильЦитата Ответить



ссылка на сообщение  Отправлено: 26.10.18 20:15. Заголовок: Возвращаясь к станда..


Возвращаясь к стандартному алгоритму std::reverse в С++, который использовали многие в вышеуказанной теме "Зачем просят перевернуть строку на интервью?" вместо того, чтобы написать C-функцию, как мною было показано, хотел бы отметить, что этот алгоритм специализирован как для двусторонних итераторов, так и для итераторов произвольного доступа.

Классический способ выполнить такую специализацию заключается в вызове в общей шаблонной функции std::reverse соответствующей специализации, которая имеет третий дополнительный параметр типа std::bidirectional_iterator_tag или std::random_access_iterator_tag в зависимости от того, какую специализацию функции следует вызвать.

Ниже представлена демонстрационная программа, которая показывает, как реализовать такой подход.
 
#include <iostream>
#include <iterator>
#include <list>
#include <vector>

template <typename Iterator>
void reverse( Iterator first, Iterator last, std::bidirectional_iterator_tag )
{
std::cout << "reverse with a bidirectional iterator is called\n";
for ( ; first != last && first != --last; ++first )
{
std::iter_swap( first, last );
}
}

template <typename Iterator>
void reverse( Iterator first, Iterator last, std::random_access_iterator_tag )
{
std::cout << "reverse with a random access iterator is called\n";
if ( first != last )
{
for ( ; first < --last; ++first )
{
std::iter_swap( first, last );
}
}
}

template <typename Iterator>
void reverse( Iterator first, Iterator last )
{
reverse( first, last, typename std::iterator_traits<Iterator>::iterator_category() );
}

int main()
{
std::list<int> lst = { 1, 2, 3, 4, 5 };

std::cout << "std::list<int>: ";
for ( int x : lst ) std::cout << x << ' ';
std::cout << '\n';

reverse( std::begin( lst ), std::end( lst ) );

std::cout << "std::list<int>: ";
for ( int x : lst ) std::cout << x << ' ';
std::cout << '\n';

std::vector<int> v = { 1, 2, 3, 4, 5 };

std::cout << "\nstd::vector<int>: ";
for ( int x : v ) std::cout << x << ' ';
std::cout << '\n';

reverse( std::begin( v ), std::end( v ) );

std::cout << "std::vector<int>: ";
for ( int x : v ) std::cout << x << ' ';
std::cout << '\n';
}

Вывод программы на консоль:
 
std::list<int>: 1 2 3 4 5
reverse with a bidirectional iterator is called
std::list<int>: 5 4 3 2 1

std::vector<int>: 1 2 3 4 5
reverse with a random access iterator is called
std::vector<int>: 5 4 3 2 1

То же самое можно проделать с функцией std::reverse, но уже используя ограничения на шаблонные параметры, появившиеся в C++17.
Вот как будет выглядеть демонстрационная программа в этом случае.
 
#include <iostream>
#include <type_traits>
#include <iterator>
#include <list>
#include <vector>

template <typename Iterator>
requires std::is_base_of_v<std::bidirectional_iterator_tag, typename std::iterator_traits<Iterator>::iterator_category> &&
std::is_base_of_v<typename std::iterator_traits<Iterator>::iterator_category, std::bidirectional_iterator_tag>
void reverse( Iterator first, Iterator last )
{
std::cout << "reverse with a bidirectional iterator is called\n";
for ( ; first != last && first != --last; ++first )
{
std::iter_swap( first, last );
}
}

template <typename Iterator>
requires std::is_base_of_v<std::random_access_iterator_tag, typename std::iterator_traits<Iterator>::iterator_category> &&
std::is_base_of_v<typename std::iterator_traits<Iterator>::iterator_category, std::random_access_iterator_tag>
void reverse( Iterator first, Iterator last )
{
std::cout << "reverse with a random access iterator is called\n";
if ( first != last )
{
for ( ; first < --last; ++first )
{
std::iter_swap( first, last );
}
}
}

int main()
{
std::list<int> lst = { 1, 2, 3, 4, 5 };

std::cout << "std::list<int>: ";
for ( int x : lst ) std::cout << x << ' ';
std::cout << '\n';

reverse( std::begin( lst ), std::end( lst ) );

std::cout << "std::list<int>: ";
for ( int x : lst ) std::cout << x << ' ';
std::cout << '\n';

std::vector<int> v = { 1, 2, 3, 4, 5 };

std::cout << "\nstd::vector<int>: ";
for ( int x : v ) std::cout << x << ' ';
std::cout << '\n';

reverse( std::begin( v ), std::end( v ) );

std::cout << "std::vector<int>: ";
for ( int x : v ) std::cout << x << ' ';
std::cout << '\n';
}

Вывод программы аналогичен выводу, показанному выше:
 
std::list<int>: 1 2 3 4 5
reverse with a bidirectional iterator is called
std::list<int>: 5 4 3 2 1

std::vector<int>: 1 2 3 4 5
reverse with a random access iterator is called
std::vector<int>: 5 4 3 2 1

В этом случае достаточно объявить лишь две функции вместо трех, как это было в предыдущем примере.

Условие требований на шаблонный аргумент алгоритма std::reverse можно записать проще, используя структуру std::is_same, объявленную в заголовке <type_traits>, как это показано в нижеследующей демонстрационной программе.
 
#include <iostream>
#include <type_traits>
#include <iterator>
#include <list>
#include <vector>

template <typename Iterator>
requires std::is_same_v<std::bidirectional_iterator_tag, typename std::iterator_traits<Iterator>::iterator_category>
void reverse( Iterator first, Iterator last )
{
std::cout << "reverse with a bidirectional iterator is called\n";
for ( ; first != last && first != --last; ++first )
{
std::iter_swap( first, last );
}
}

template <typename Iterator>
requires std::is_same_v<std::random_access_iterator_tag, typename std::iterator_traits<Iterator>::iterator_category>
void reverse( Iterator first, Iterator last )
{
std::cout << "reverse with a random access iterator is called\n";
if ( first != last )
{
for ( ; first < --last; ++first )
{
std::iter_swap( first, last );
}
}
}

int main()
{
std::list<int> lst = { 1, 2, 3, 4, 5 };

std::cout << "std::list<int>: ";
for ( int x : lst ) std::cout << x << ' ';
std::cout << '\n';

reverse( std::begin( lst ), std::end( lst ) );

std::cout << "std::list<int>: ";
for ( int x : lst ) std::cout << x << ' ';
std::cout << '\n';

std::vector<int> v = { 1, 2, 3, 4, 5 };

std::cout << "\nstd::vector<int>: ";
for ( int x : v ) std::cout << x << ' ';
std::cout << '\n';

reverse( std::begin( v ), std::end( v ) );

std::cout << "std::vector<int>: ";
for ( int x : v ) std::cout << x << ' ';
std::cout << '\n';
}

Вывод программы на консоль:
 
std::list<int>: 1 2 3 4 5
reverse with a bidirectional iterator is called
std::list<int>: 5 4 3 2 1

std::vector<int>: 1 2 3 4 5
reverse with a random access iterator is called
std::vector<int>: 5 4 3 2 1

Данные программы можно протестировать с помощью он-лайнового компилятора по адресу https://wandbox.org. Следует не забыть указать опцию компилятора -fconcepts.

Спасибо: 0 
ПрофильЦитата Ответить



ссылка на сообщение  Отправлено: 08.11.18 16:39. Заголовок: Вообще говоря, в нас..


Вообще говоря, в настоящее время имеется три подхода по определению алгоритма std::reverse. (Если вы знаете еще один способ, то обнародуйте его в этой теме:)).

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

 
#include <iostream>
#include <iterator>
#include <list>
#include <vector>

template <typename Iterator>
void reverse( Iterator first, Iterator last, std::bidirectional_iterator_tag )
{
std::cout << "reverse with a bidirectional iterator is called\n";
for ( ; first != last && first != --last; ++first )
{
std::iter_swap( first, last );
}
}

template <typename Iterator>
void reverse( Iterator first, Iterator last, std::random_access_iterator_tag )
{
std::cout << "reverse with a random access iterator is called\n";
if ( first != last )
{
for ( ; first < --last; ++first )
{
std::iter_swap( first, last );
}
}
}

template <typename Iterator>
void reverse( Iterator first, Iterator last )
{
reverse( first, last, typename std::iterator_traits<Iterator>::iterator_category() );
}

int main()
{
std::list<int> lst = { 1, 2, 3, 4, 5 };

std::cout << "std::list<int>: ";
for ( int x : lst ) std::cout << x << ' ';
std::cout << '\n';

reverse( std::begin( lst ), std::end( lst ) );

std::cout << "std::list<int>: ";
for ( int x : lst ) std::cout << x << ' ';
std::cout << '\n';

std::vector<int> v = { 1, 2, 3, 4, 5 };

std::cout << "\nstd::vector<int>: ";
for ( int x : v ) std::cout << x << ' ';
std::cout << '\n';

reverse( std::begin( v ), std::end( v ) );

std::cout << "std::vector<int>: ";
for ( int x : v ) std::cout << x << ' ';
std::cout << '\n';
}


Второй подход состоит в использовании ограничений (или требований) на шаблонные аргументы.
 
#include <iostream>
#include <type_traits>
#include <iterator>
#include <list>
#include <vector>

template <typename Iterator>
requires std::is_same_v<std::bidirectional_iterator_tag, typename std::iterator_traits<Iterator>::iterator_category>
void reverse( Iterator first, Iterator last )
{
std::cout << "reverse with a bidirectional iterator is called\n";
for ( ; first != last && first != --last; ++first )
{
std::iter_swap( first, last );
}
}

template <typename Iterator>
requires std::is_same_v<std::random_access_iterator_tag, typename std::iterator_traits<Iterator>::iterator_category>
void reverse( Iterator first, Iterator last )
{
std::cout << "reverse with a random access iterator is called\n";
if ( first != last )
{
for ( ; first < --last; ++first )
{
std::iter_swap( first, last );
}
}
}

int main()
{
std::list<int> lst = { 1, 2, 3, 4, 5 };

std::cout << "std::list<int>: ";
for ( int x : lst ) std::cout << x << ' ';
std::cout << '\n';

reverse( std::begin( lst ), std::end( lst ) );

std::cout << "std::list<int>: ";
for ( int x : lst ) std::cout << x << ' ';
std::cout << '\n';

std::vector<int> v = { 1, 2, 3, 4, 5 };

std::cout << "\nstd::vector<int>: ";
for ( int x : v ) std::cout << x << ' ';
std::cout << '\n';

reverse( std::begin( v ), std::end( v ) );

std::cout << "std::vector<int>: ";
for ( int x : v ) std::cout << x << ' ';
std::cout << '\n';
}


И, наконец, третий подход состоит в использовании требований на шаблонные аргументы, а также предложения if с constexpr условием.
 
#include <iostream>
#include <type_traits>
#include <iterator>
#include <list>
#include <vector>

template <typename BidirectionalIterator>
requires std::is_base_of_v<std::bidirectional_iterator_tag, typename std::iterator_traits<BidirectionalIterator>::iterator_category>
void reverse( BidirectionalIterator first, BidirectionalIterator last )
{
if constexpr( std::is_same_v<std::random_access_iterator_tag, typename std::iterator_traits<BidirectionalIterator>::iterator_category> )
{
std::cout << "reverse with a random access iterator is called\n";
if ( first != last )
{
for ( ; first < --last; ++first )
{
std::iter_swap( first, last );
}
}
}
else
{
std::cout << "reverse with a bidirectional iterator is called\n";
for ( ; first != last && first != --last; ++first )
{
std::iter_swap( first, last );
}
}
}

int main()
{
std::list<int> lst = { 1, 2, 3, 4, 5 };

std::cout << "std::list<int>: ";
for ( int x : lst ) std::cout << x << ' ';
std::cout << '\n';

reverse( std::begin( lst ), std::end( lst ) );

std::cout << "std::list<int>: ";
for ( int x : lst ) std::cout << x << ' ';
std::cout << '\n';

std::vector<int> v = { 1, 2, 3, 4, 5 };

std::cout << "\nstd::vector<int>: ";
for ( int x : v ) std::cout << x << ' ';
std::cout << '\n';

reverse( std::begin( v ), std::end( v ) );

std::cout << "std::vector<int>: ";
for ( int x : v ) std::cout << x << ' ';
std::cout << '\n';
}

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

Спасибо: 0 
ПрофильЦитата Ответить



ссылка на сообщение  Отправлено: 19.02.20 13:41. Заголовок: Вот еще один аналоги..


Вот еще один аналогичный вопрос на Stackoverflow относительно инверсии отдельных слов в символьном массиве. На этот раз требуется выполнить задачу на языке программирования C Reverse a string without strtok in C

Ниже представлена демонстрационная программа, в которой показано, как можно выполнить поставленную задачу.
 
#include <stdio.h>
#include <string.h>

static char * reverse( char *s, size_t n )
{
for ( size_t i = 0; i < n / 2; i++ )
{
char c = s[ i ];
s[ i ] = s[ n - i - 1 ];
s[ n - i - 1 ] = c;
}

return s;
}

char * reverse_by_words( char *s )
{
const char *delim = " \t";

char *p = s;

while ( *p )
{
p += strspn( p, delim );

if ( *p )
{
char *q = p;

p += strcspn( p, delim );

reverse( q, p - q );
}
}

return reverse( s, p - s );
}

int main(void)
{
char s[] = "5 60 +";

puts( s );

puts( reverse_by_words( s ) );

return 0;
}

Вывод программы на консоль:
 
5 60 +
+ 60 5

Если нужно сохранить ведущие и замыкающие пробелы в строке в том виде, как они присутствуют в исходной строке, то это можно сделать следующим образом.,как показано в демонстрационной программе ниже.
 
#include <stdio.h>
#include <string.h>

static char *reverse( char *s, size_t n )
{
for ( size_t i = 0; i < n / 2; i++ )
{
char c = s;
s = s[n - i -1 ];
s[n - i - 1] = c;
}

return s;
}

char * reverse_by_words( char *s )
{
const char *delim = " \t";

char *first = s, *last = s;

for ( char *p = s; *p; )
{
p += strspn( p, delim );

if ( last == s ) first = last = p;


if ( *p )
{
char *q = p;

p += strcspn( p, delim );

last = p;

reverse( q, p - q );
}
}

reverse( first, last - first );

return s;
}

int main(void)
{
char s[] = "\t\t\t5 60 +";

printf( "\"%s\"\n", s );

printf( "\"%s\"\n", reverse_by_words( s ) );

return 0;
}

Вывод программы на консоль:
 
" 5 60 +"
" + 60 5"


Спасибо: 0 
ПрофильЦитата Ответить
Ответ:
1 2 3 4 5 6 7 8 9
большой шрифт малый шрифт надстрочный подстрочный заголовок большой заголовок видео с youtube.com картинка из интернета картинка с компьютера ссылка файл с компьютера русская клавиатура транслитератор  цитата  кавычки моноширинный шрифт моноширинный шрифт горизонтальная линия отступ точка LI бегущая строка оффтопик свернутый текст

показывать это сообщение только модераторам
не делать ссылки активными
Имя, пароль:      зарегистрироваться    
Тему читают:
- участник сейчас на форуме
- участник вне форума
Все даты в формате GMT  3 час. Хитов сегодня: 31
Права: смайлы да, картинки да, шрифты да, голосования нет
аватары да, автозамена ссылок вкл, премодерация откл, правка нет