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

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



ссылка на сообщение  Отправлено: 28.07.12 19:10. Заголовок: Два языка С++ в одном языке!


С введением нового стандарта С++ 2011 язык С++ настолько изменился, что, фактически, в нем сосуществуют два различных языка, сильно отличающихся друг от друга.
Эта мысль у меня возниклла, когда я решил сам для себя сделать простое задание, опубликованное некоторым студентом на одном из форумов со слезной просьбой помочь ему его сделать.

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

Как бы решал эту задачу студент? Очевидно,что ему нужна функция, которая вычисляет среднее арифметическое строки матрицы. Функция могла бы выглядеть так:

int average( int a[], size_t n ) 
{
int sum = 0;
int count = 0;

for ( size_t i = 0; i < n; i++ )
{
if ( a[ i ] != 0 )
{
sum += a[ i ];
count++;
}
}
if ( count != 0 ) sum /= count;

return ( sum );
}



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

void replace_zero( int a[], size_t n, int value ) 
{
for ( size_t i = 0; i < n; i++ )
{
if ( a[ i ] == 0 ) a[ i ] = value;
}
}


Теперь осталось лишь организовать главный цикл по строкам двумерной матрицы. Этот цикл мог бы выглядеть таким образом, Предположим, что исходная матрица объявлена как int a[m][n];

for ( size_t i = 0; i < m; i++ ) 
{
int value = average( a[ i ], n );
if ( value != 0 ) replace_zero( a[ i ], n, value );
}


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

for ( size_t i = 0; i < m; i++ ) 
{
for ( j = 0; j < n; j++ )
{
std::cout << a[ i ][ j ] << ' ';
}
std::cout << std::endl;
}

std::cout << std::endl;


Теперь можно все собрать вместе. Для простоты матрица будет задаваться списком инициализации при ее объявлении.

#include  <iostream> 

int average( int a[], size_t n )
{
int sum = 0;
int count = 0;

for ( size_t i = 0; i < n; i++ )
{
if ( a[ i ] != 0 )
{
sum += a[ i ];
count++;
}
}

if ( count != 0 ) sum /= count;

return ( sum );
}

void replace_zero( int a[], size_t n, int value )
{
for ( size_t i = 0; i < n; i++ )
{
if ( a[ i ] == 0 ) a[ i ] = value;
}

}

int main()
{
const size_t m = 3;
const size_t n = 14;
int a[ m ][ n ] = { { 1, 0, 3, 4, 0, 0, 7, 3, 0, 9, 5, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 7, 5, 0, 0, 0, 11, 1, 4, 0, 2, 0, 0, 1, 9 } };

for ( size_t i = 0; i < m; i++ )
{
for ( j = 0; j < n; j++ )
{
std::cout << a[ i ][ j ] << ' ';
}
std::cout << std::endl;
}
std::cout << std::endl;

for ( size_t i = 0; i < m; i++ )
{
int value = average( a[ i ], n );
if ( value != 0 ) replace_zero( a[ i ], n, value );
}

for ( siize_t i = 0; i < m; i++ )
{
for ( j = 0; j < n; j++ )
{
std::cout << a[ i ][ j ] << ' ';
}
std::cout << std::endl;
}
std::cout << std::endl;

return ( 0 );
}


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

Итак, это один язык программирования в языке С++.

А, вот, второй язык пррограммирования в языке С++!

#include	<iostream> 
#include <iterator>
#include <algorithm>
#include <numeric>
#include <functional>

template <typename Container>
struct replace_zero: std::unary_function<Container, void>
{
void operator ()( Container &c ) const
{
int n = std::count_if( std::begin( c ), std::end( c ),
std::bind2nd( std::not_equal_to<int>(), 0 ) );

if ( n )
{
std::replace( std::begin( c ), std::end( c ), 0,
std::accumulate( std::begin( c ), std::end( c ), 0 ) / n );
}
}
};

int main()
{
int a[][14] = { { 1, 0, 3, 4, 0, 0, 7, 3, 0, 9, 5, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 7, 5, 0, 0, 0, 11, 1, 4, 0, 2, 0, 0, 1, 9 } };

for ( auto it = std::begin( a ); it != std::end( a ); ++it )
{
std::copy ( std::begin( *it ), std::end( *it ),
std::ostream_iterator<int>( std::cout, " " ) );
std::cout << std::endl;
}
std::cout << std::endl;

std::for_each( std::begin( a ), std::end( a ), replace_zero<decltype( a[0] )>() );

for ( auto it = std::begin( a ); it != std::end( a ); ++it )
{
std::copy ( std::begin( *it ), std::end( *it ),
std::ostream_iterator<int>( std::cout, " " ) );
std::cout << std::endl;
}
std::cout << std::endl;

return ( 0 );
}


Соверршенно два разных языка, не так ли?!

Более того во втором варианте я мог бы написать вместо предложения

	for ( auto it = std::begin( a ); it != std::end( a ); ++it )


следующую конструкцию, если бы MS VC++ 2010 ее поддерживал ( я использовал этот компилятор для второго варианта)

	for ( const auto &it : a )


Также хочу обратить внимание, что сначала у меня было записано в одном предложении таким образом

	std::for_each( std::begin( a ), std::end( a ), replace_zero<decltype( char[14])>() );


но затем я его поменял на предложение с decltype:

	std::for_each( std::begin( a ), std::end( a ), replace_zero<decltype( a[0] )>() );


Как видите, между первым и вторым вариантом существует огромная пропасть! Теперь время изучения С++ значительно увеличивается. А я вспоминаю те времена, когда использовались Clipper Summer 85, Dbase III и т.д. Тогда мой один знакомый программист хвастался: "Я этот С++ за один вечер выучил!"

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





ссылка на сообщение  Отправлено: 28.07.12 19:10. Заголовок: А вот еще один нагля..


А вот еще один наглядный пример, демонстрирующий сосуществование двух языков в С++ в одном языке. Постановка задачи примера также заимствована из задания для студентов. Звучит она просто. Пусть заданы два целочисленных массива a[1..M] и b[1...N]. Требуется подсчитать, сколько имеется общих элементов, которые содержатся в обоих массивах.
Я не буду писать решение, которое для данного задания написал бы студент, используя первый из "двух" языков С++, в котором нет стандартных алгоритмов, итераторов, лямбда-выражений и.т.д.. Каждый сам хотя бы на минуту может задуматься, как это задание выполнить с помощью обычных циклов, прежде чем читать дальше. По крайней мере каждый может представить себе, как такое решение будет выглядеть при использовании "первого" языка С++.
Я сразу же приведу решение, написанное на "втором" языке С++. Его будет вполне достаточно, чтобы прочувствовать разницу между "первым" и "вторым" языком С++. Программа написана на MS VC++ 2010.

#include "stdafx.h" 
#include <iostream>
#include <iterator>
#include <algorithm>
#include <vector>
#include <cstdlib>

int _tmain(int argc, _TCHAR* argv[])
{
const int N = 20;
const int M = 15;
int a[N];
int b[M];

std::generate_n( a, N, [=]{ return ( std::rand() % N ); } );
std::generate_n( b, M, [=]{ return ( std::rand() % M ); } );

std::sort( a, a + N );
std::sort( b, b + M );

std::cout << "a[] = { ";
std::for_each( a, a + N, []( int x ){ std::cout << x << ", "; } );
std::cout << "};" << std::endl;
std::cout << "b[] = { ";
std::for_each( b, b + M, []( int x ){ std::cout << x << ", "; } );
std::cout << "};" << std::endl;

std::vector<int> c;
std::set_intersection( a, a + N, b, b + M, std::back_inserter( c ) );

std::cout << "c[] = { ";
std::for_each( c.begin(), c.end(), []( int x ){ std::cout << x << ", "; } );
std::cout << "};" << std::endl;
std::cout << "Number of common elemennts is " << c.size() << std::endl;

return( 0 );
}


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



ссылка на сообщение  Отправлено: 28.07.12 19:12. Заголовок: Еще один пример на э..


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

Это задание я нашел на форуме cplusplus.com здесь.

Исходный текст задания простой


 цитата:
Suppose A, B, C are arrays of integers of size M, N, and M + N respectively. The
numbers in array A appear in ascending order while the numbers in array B appear in descending order. Write a user defined function in C++ to produce third array C by merging arrays A and B in ascending order. Use A, B and C as arguments in the function.



В переводе это задание звучит так: даны три массива A, B, C соответственно размерностью M, N и M + N. В массиве A данные упорядочены по возрастанию, а в массиве B - по убыванию. Написать определенную поользователем функцию, которая в массиве C объединяет элементы из массива A и B, упорядоченные по возрастанию. Испльзовать A, B и C как аргументы функции.

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

Поэтому студент естественно проигнорирует предложенное готовое решение, а будет мучаться с написанием многочисленнных циклов. На то он и студент, чтобы мучаться!

Я же покажу, как решение задачи может выглядеть с использованием новых синтаксических конструкций С++.

 #include <iostream> 
#include <algorithm>
#include <numeric>
#include <iterator>

template <size_t M, size_t N>
void merge( const int ( &a )[M], const int ( &b )[N], int ( &c )[M+N] )
{
std::merge( std::begin( a ), std::end( a ),
std::reverse_iterator<const int *>( std::end( b ) ),
std::reverse_iterator<const int *>( std::begin( b ) ),
std::begin( c ) );
}

void ( *out )( int ) = [] ( int x ) { std::cout << x << ' '; };

int main()
{
const int M = 10;
const int N = 20;

int a[M];
int b[N];
int c[M+N];

std::iota( std::begin( a ), std::end( a ), M / 2 );
std::iota( std::begin( b ), std::end( b ), 0 );
std::reverse( std::begin( b ), std::end( b ) );

for ( auto i : a ) out( i );
std::cout << std::endl;
for ( auto i : b ) out( i );
std::cout << std::endl;

merge( a, b, c );

for ( auto i : c ) out( i );
std::cout << std::endl;

return ( 0 );
}


Ситуация усугубляется еще тем, что не все современные компиляторы в состоянии успешно скомпилировать этот код! Например, MS VC++ 2010 этого сделать не может. Но убедиться в корректности кода можно с помощью он-лайнового компилятора

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



ссылка на сообщение  Отправлено: 28.07.12 19:12. Заголовок: Только сейчас замети..


Только сейчас заметил, что в последнем примере включение алгоритма std::reverse в код программы было излишним. Вполне достаточно было обойтись алгоритмом std::iota, просто задав реверсивный итератор. То есть данный фрагмент кода

 
std::iota( std::begin( a ), std::end( a ), M / 2 );
std::iota( std::begin( b ), std::end( b ), 0 );
std::reverse( std::begin( b ), std::end( b ) );


можно было упростить следующим образом:

 
std::iota( std::begin( a ), std::end( a ), M / 2 );
std::iota( std::reverse_iterator<int *>( std::end( b ) ),
std::reverse_iterator<int *>( std::begin( b ) ), 0 );


Но в конечном итоге это дело вкуса.

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

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