C# для чайников
.pdfII...генерируем сообщение об ошибке .. .
Console . WriteLine("Вкла д не может быть отрицателен") ;
} |
|
|
|
e l s e |
|
|
|
{ |
|
|
|
// |
. . . в противном случае просим ввести процентную |
||
// |
ставку |
|
|
Console . Write("Введит е процентную с т а в к у : " ) ; |
|||
s t r i n g s l n t e r e s t = C o n s o l e . R e a d L i n e ( ) ; |
|||
decimal |
m l n t e r e s t = |
C o n v e r t . T o D e c i m a l ( s l n t e r e s t ) ; |
|
// |
Если |
процентная |
ставка отрицательна или слишком |
// велика. . . |
|
||
if |
( m l n t e r e s t < О |
I m l n t e r e s t > nMaximumlnterest) |
{
//. . . генерируем сообщение об ошибке
Console . WriteLine("Процентна я ставка не может " + "быть отрицательна " + "или превышать " +
n M a x i m u m l n t e r e s t ) ;
m l n t e r e s t = 0;
}
e l s e
{
/ / И величина вклада, и |
процентная |
ставка |
|
// |
корректны — запрашиваем у пользователя срок, |
||
// |
для которого следуе т |
вычислить |
величины вкладов |
//с начисленными процентами
C o n s o l e . W r i t e ( " В в е д и т е количество л е т : " ) ; s t r i n g s D u r a t i o n = C o n s o l e . R e a d L i n e ( ) ;
i n t nDuratio n = C o n v e r t . T o I n t 3 2 ( s D u r a t i o n ) ;
//Выводим введенные величины
|
C o n s o l e . W r i t e L i n e ( ) ; |
// |
Пропуск |
строки |
|||
|
C o n s o l e . W r i t e L i n e ( " В к л а д = " |
|
|||||
|
|
|
|
+ m P r i n c i p a l ) ; |
|
||
|
Console . WriteLine("Процент ы = " |
|
|||||
|
|
|
|
+ m l n t e r e s t + |
"%") ; |
||
|
C o n s o l e . W r i t e L i n e ( " С р о к |
= " |
|
||||
|
|
|
|
+ nDuratio n + |
" y e a r s " ) ; |
||
|
C o n s o l e . W r i t e L i n e ( ) ; |
|
|
|
|||
|
// |
Цикл |
по |
указанному |
пользователем количеству лет |
||
|
i n t |
nYear |
= |
1; |
|
|
|
|
w h i l e ( n Y e a r |
<= nDuration ) |
|
|
|||
|
{ |
|
|
|
|
|
|
|
// Вычисление вклада с начисленными процентами |
||||||
|
decima l m l n t e r e s t P a i d ; |
|
|
||||
|
m l n t e r e s t P a i d = m P r i n c i p a l * ( m l n t e r e s t / 1 0 0 ) ; |
||||||
|
// Вычисляем новое |
значение вклада |
|||||
|
m P r i n c i p a l = m P r i n c i p a l + m l n t e r e s t P a i d ; |
||||||
в C# |
^ава 5. Управление потоком выполнения |
|
95 |
// Округляем |
величину до копеек |
m P r i n c i p a l = |
d e c i m a l . R o u n d ( m P r i n c i p a l , 2 ) ; |
//Выводим результа т
C o n s o l e . W r i t e L i n e ( n Y e a r + " - " + m P r i n c i p a l )
// Переходим к следующему год у nYear = nYear + 1;
}
}
// Ожидаем подтверждения пользователя Console . WriteLine("Нажмит е <Enter> для "
"завершения программы. ") ;
C o n s o l e . R e a d ( ) ;
}
}
Вот как выглядит вывод программы C a l c u l a t e l n t e r e s t T a b l e :
Введите сумму вклада :123 4
Введите |
процентную |
ставку : 1 2 . 5 |
Введите |
количество |
л е т : 1 0 |
Вклад |
= 1234 |
|
Проценты = 12 . 5% |
|
|
Срок |
= l O y e a r s |
|
1 - 1 3 8 8 . 2 5
2 - 1 5 6 1 . 7 8
3 - 1 7 5 7 . 0 0
4 - 1 9 7 6 . 6 2
5 - 2 2 2 3 . 7 0
6 - 2 5 0 1 . 6 6
7 - 2 8 1 4 . 37
8 - 3 1 6 6 . 1 7
9 - 3 5 6 1 . 9 4 1 0 - 4 0 0 7 . 1 8
Нажмите <Enter> для завершения программы...
Каждое значение представляет общую сумму вклада по истечении указанного срока i предположении, что начисленные проценты добавляются к основному вкладу. Так, сум ма в 1234 грн. при ставке 12.5% за 9 лет превращается в 3561.94 грн.
В большинстве значений для количества копеек выделяется две цифры. Однако в некоторых версиях С# завершающие нули могут не выводиться, и, например, сумма 2223.70 может оказаться выведенной как 2223.7. Это поведение 0 можно исправить с помощью специальных форматирующих символов, опис ваемых в главе 9, "Работа со строками в С#" (С# 2.0 выводит завершающие нули по умолчанию.)
Программа C a l c u l a t e l n t e r e s t T a b l e начинает работу со считывания величины] вклада и процентной ставки и проверки их корректности. Затем программа считывает количество лет, для которых надо просчитать величины вкладов, и сохраняет их в пере менной nDuration .
96 |
Часть II. Основы программирования в С* |
|
Перед тем как войти в цикл w h i l e , программа объявляет переменную nYear, ини циализированную значением 1. Эта переменная будет "текущим годом", т.е. ее значение будет увеличиваться с каждым выполнением тела цикла. Если номер года, хранящийся в переменной nYear, меньше общего количества лет, хранящегося в переменной nDuration, величина вклада для "этого года" вычисляется исходя из процентной ставки и ве личины вклада в "предыдущем году". Вычисленное значение программа выводит вместе со значением "текущего года".
Инструкция d e c i m a l . Round () округляет вычисленное значение до копеек.
Ключевая часть программы находится в последней строке тела цикла. Выражение nYear = nYear + 1; увеличивает переменную nYear на 1. После увеличения значе ния года управление передается в начало цикла, где величина, хранящаяся в nYear, срав нивается с запрошенным количеством лет. В данном примере этот с р о к — 10 лет, так что когда значение переменной nYear станет равным 11, т.е. превысит 10, программа передаст управление первой строке после цикла w h i l e , и работа цикла на этом прекратится.
Большинство команд циклов используют этот базовый принцип увеличения значения счетчика, пока оно не достигнет некоторой предварительно заданной величины.
Переменная-счетчик nYear в программе C a l c u l a t e l n t e r e s t T a b l e должна быть объявлена и инициализирована до цикла w h i l e , в котором она используется. Кроме то го, переменная nYear должна увеличиваться, обычно в последнем выражении тела цик ла. Как показано в примере, вы должны заранее позаботиться о том, какие переменные вам понадобятся в цикле. После того как вы напишете пару тысяч циклов w h i l e , все это будет делаться автоматически.
При написании цикла w h i l e не забывайте увеличивать значение счетчика. Взгляните на приведенный пример исходного текста:
i n t nYear = |
1; |
|
w h i l e (nYear |
< |
10) |
{ |
|
|
/ / . . . Какой-то код . . .
}
(Я сознательно забыл написать nYear = nYear + 1;.) Без увеличения счетчика переменная nYear всегда содержит значение 1, так что цикл работает вечно. Такая ситуация называется зацикливанием (infinite loop). Единственный способ прекратить зацикливание — аварийно завершить программу извне (или перезагрузить компьютер).
Убедитесь, что условие прекращения работы цикла может быть достигнуто. Обычно это означает корректное увеличение счетчика цикла. В противном слу чае вы получите зацикливание программы, недовольных пользователей, паде ние продаж и много прочих неприятностей...
Глава 5. Управление потоком выполнения |
97 |
Зацикливание — достаточно распространенная ошибка, так что не слишком расстраивайтесь, допустив ее в своей программе.
Ц и к л do...while
Разновидностью цикла w h i l e можно считать цикл do . . . while . При его использо| вании условие не проверяется, пока не будет достигнут конец цикла:
i n t nYear = 1;
do |
|
|
{ |
|
|
/ / |
. . . Некоторые вычисления . . . |
|
nYear = nYear |
+ 1; |
|
} w h i l e |
(nYear < |
n D u r a t i o n ) ; |
В противоположность циклу w h i l e , тело цикла do . . . w h i l e всегда выполняется крайней мере один раз, независимо от значения переменной nDuration . Этот ци встречается в реальных программах гораздо реже, чем цикл w h i l e .
О п е р а т о р ы break и continue
Для управления циклом имеются два специальных оператора — break и continue Оператор break вызывает прекращение выполнения цикла и передачу управления пер вому выражению непосредственно за циклом. Команда c o n t i n u e передает управленш в начало цикла, к проверке его условия.
Лично я редко пользуюсь оператором c o n t i n u e , так что иногда просто за бываю о его существовании. Думаю, что есть немало программистов, посту пающих так же.
Предположим, вы хотите прекратить выполнение рассматривавшейся ранее програм мы, как только сумма вклада превысит начальную в некоторое заранее заданное числ( раз, независимо от того, какой срок прошел до этого момента. Это можно легко сделан добавив в тело цикла следующие строки:
if (mPrincipa l > (maxPower * m O r i g i n a l P r i n c i p a l ) )
{
break ;
}
Оператор break не будет выполняться, пока условие оператора if не станет истин ным, т.е. пока вычисленная величина вклада не превысит исходную в maxPower раз. Ons
ратор break передаст управление за |
пределы цикла w h i l e (nYear<=nDurationj |
и выполнение программы продолжится |
с выражения, следующего непосредственно з |
этим циклом. |
|
Полный текст этой программы можно найти на прилагаемом компакт-диен в папке C a l c u l a t e l n t e r e s t T a b l e W i t h B r e a k (он здесь не приводите для краткости изложения).
98 |
Часть II. Основы программирования в С |
Введите |
cyjvijviy вклада |
i loo |
|
|
В в е д и т е п р о ц е н т н у ю с т а в к у |
: 2 5 |
|||
Введите |
количество л е т : 1 0 0 |
|
||
Вклад |
= |
10 0 |
|
|
Проценты = |
25% |
|
|
|
Срок |
= |
100 y e a r s |
|
|
Выход по достижении коэффициента |
10 |
1-125.00
2-156.25
3-195.31
4-244.14
5-305.18
6-381.48
7-476.85
8-596 . 06
9-745 . 08
10-931.35
11-1164 . 19
Нажмите <Enter> для завершения программы...
Программа прекращает работу, как только вычисленное значение вклада превышает 1000 — к счастью, этого не надо дожидаться 100 лет!
Цикл без с ч е т ч и к а
Программа C a l c u l a t e l n t e r e s t T a b l e достаточно интеллектуальна для того, чтобы завершить работу, если пользователь ввел неверное значение вклада или процент ной ставки. Однако трудно назвать дружественной программу, сразу же прекращающую работу, не давая пользователю ни одного шанса на исправление ошибки.
Комбинация w h i l e и break позволяет сделать программу немного более гибкой, что можно увидеть на примере исходного текста программы Cal - c u l a t e l n t e r e s t T a b l e M o r e F o r g i v i n g :
//C a l c u l a t e I n t e r e s t T a b l e M o r e F o r g i v i n g
//Вычисление величины начисленных процентов для данного
//вклада за определенный период времени. Программа
//позволяет пользователю исправить ошибку ввода величины
//вклада и процентной ставки
using System;
namespace C a l c u l a t e I n t e r e s t T a b l e M o r e F o r g i v i n g
[{
using System ;
public c l a s s Program
{
public s t a t i c v o i d Main ( s t r i n g [ ] a r g s )
{
// Определяем максимально возможное значение
Глава 5. Управление потоком выполнения |
99 |
// |
процентной ставки |
i n t |
nMaximumlnterest = 5 0 ; |
//Приглашение пользователю ввести величину исходного
// |
вклада; |
повторяем |
эт о приглашение до те х пор, пока |
// |
не буде т |
получено |
корректное значение |
decima l m P r i n c i p a l ; w h i l e ( t r u e )
{
C o n s o l e . W r i t e ( " В в е д и т е сумму в к л а д а : " ) ; s t r i n g s P r i n c i p a l = C o n s o l e . R e a d L i n e ( ) ;
m P r i n c i p a l = C o n v e r t . T o D e c i m a l ( s P r i n c i p a l ) ;
// |
Выход из цикла, |
если введенное значение корректно |
if |
(mPrincipa l >= |
0) |
{
break ;
}
// Генерируем сообщение |
о неверном |
вводе |
C o n s o l e . W r i t e L i n e ( " В к л а д |
не может |
быть отрицателен") ; |
C o n s o l e . W r i t e L i n e ( " П о в т о р и т е в в о д " ) ; C o n s o l e . W r i t e L i n e ( ) ;
}
// Теперь вводим величину процентной ставки decima l m l n t e r e s t ;
w h i l e ( t r u e )
{
C o n s o l e . W r i t e ( " В в е д и т е процентную с т а в к у : " ) ; s t r i n g s l n t e r e s t = C o n s o l e . R e a d L i n e ( ) ;
m l n t e r e s t = C o n v e r t . T o D e c i m a l ( s l n t e r e s t ) ;
// |
Если процентная |
ставка отрицательна или слишком |
/ / в е л и к а . . . |
|
|
if |
( m l n t e r e s t >= 0 |
&& m l n t e r e s t <= nMaximumlnterest) |
{
break ;
}
//. . . генерируем сообщение об ошибке
Console . WriteLine("Процентна я ставка не может " + "быть отрицательна " + "или превышать " +
n M a x i m u m l n t e r e s t ) ; C o n s o l e . W r i t e L i n e ( " П о в т о р и т е в в о д " ) ; C o n s o l e . W r i t e L i n e ( ) ;
}
/ / И величина вклада, и процентная ставка
//корректны — запрашиваем у пользователя срок,
// |
для которого следуе т вычислить величины вкладов |
// |
с начисленными процентами |
100 |
Часть II. Основы программирования в С# |
|
C o n s o l e . W r i t e ( " В в е д и т е количество л е т : " ) ; s t r i n g s D u r a t i o n = C o n s o l e . R e a d L i n e ( ) ;
i n t n D u r a t i o n = C o n v e r t . T o I n t 3 2 ( s D u r a t i o n ) ;
//Выводим введенные величины
C o n s o l e . W r i t e L i n e ( ) ; // |
Пропуск строки |
C o n s o l e . W r i t e L i n e ( " В к л а д |
= " + m P r i n c i p a l ) ; |
Console . WriteLine("Процент ы = |
" + |
|
m l n t e r e s t |
+ |
"%") ; |
C o n s o l e . W r i t e L i n e ( " С р о к |
= " + n D u r a t i o n |
"y e a r s " ) ;
C o n s o l e . W r i t e L i n e ( ) ;
// |
Цикл |
по |
указанному пользователем количеству лет |
i n t |
nYear |
= |
1; |
w h i l e ( n Y e a r |
<= nDuration ) |
{
// Вычисление вклада с начисленными процентами decima l m l n t e r e s t P a i d ;
m l n t e r e s t P a i d = m P r i n c i p a l * ( m l n t e r e s t / 1 0 0 ) ;
// Вычисляем новое значение вклада
m P r i n c i p a l = m P r i n c i p a l + m l n t e r e s t P a i d ;
// Округляем величину до копеек
m P r i n c i p a l = d e c i m a l . R o u n d ( m P r i n c i p a l , 2 ) ;
//Выводим результа т
C o n s o l e . W r i t e L i n e ( n Y e a r + " - " + m P r i n c i p a l ) ;
// Переходим к следующему году nYear = nYear + 1;
}
// Ожидаем подтверждения пользователя Console . WriteLine("Нажмит е <Enter> для " +
"завершения программы ... ") ;
\I }} C o n s o l e . R e a d ( ) ;
}
Данная программа во многом похожа на предыдущие примеры, за исключением ис ходного текста пользовательского ввода. В этот раз оператор if, выявлявший неверный
ввод, заменен циклом w h i l e :
decimal m P r i n c i p a l ; while(true)
{
Console . Write("Введит е сумму в к л а д а : " ) ; s t r i n g s P r i n c i p a l = C o n s o l e . R e a d L i n e ( ) ;
mPrincipal = C o n v e r t . T o D e c i m a l ( s P r i n c i p a l ) ;
Глава 5. Управление потоком выполнения |
101 |
// |
Выход из цикла, |
если введенное значение корректно |
if |
(mPrincipa l >= |
0) |
{
break;
}
// Генерируем сообщение |
о неверном |
вводе |
C o n s o l e . W r i t e L i n e ( " В к л а д |
не может |
быть о т р и ц а т е л е н " ) ; |
C o n s o l e . W r i t e L i n e ( " П о в т о р и т е в в о д " ) ; C o n s o l e . W r i t e L i n e ( ) ;
}
В представленном фрагменте кода пользовательский ввод выполняется в цикле. Если! введенное значение корректно, программа выходит из цикла и продолжает выполнение.! Однако если в пользовательском вводе имеется ошибка, пользователь получает сообще-| ние о ней, и управление передается в начало цикла.
Обратите внимание на отсутствие условия в этом цикле, вернее, на то, что оно! всегда истинно, а выход из цикла происходит при проверке условия в его теле. I
Обратите также внимание на обращение условия, поскольку теперь проблема не в| том, чтобы вывести сообщение об ошибке при некорректном вводе, а в том, чтобы за-1
вершить цикл при корректном. Проверка условия |
|||
m l n t e r e s t |
< 0 |
|| m l n t e r e s t > |
nMaximumlnterest |
превратилась в проверку |
|
||
m l n t e r e s t |
>= 0 |
&& m l n t e r e s t |
<= nMaximumlnterest |
Понятно, |
что |
условие m l n t e r e s t > = 0 противоположно условию mlnterest<0, |
Менее очевидна замена оператора ИЛИ (| |) оператором И (&&). Теперь оператор if гласит: "Выйти из цикла, если процентная ставка не меньше нуля И не больше макси-1 мального значения (другими словами, имеет корректную величину)".
Кстати, каким образом можно изменить программу C a l c u l a t e l n t e r e s t T a - l b l e M o r e F o r g i v i n g так, чтобы пользователь мог проводить вычисление за вычисле-1 нием, вводя все новые значения вклада и процентной ставки, пока не захочет завершить
работу с программой? Подсказка: для |
этого можно использовать еще один |
цикл |
w h i l e ( t r u e ) со своим собственным условием выхода. |
|
|
И последнее замечание: переменная |
m P r i n c i p a l должна быть объявлена за |
преде-1 |
лами цикла в соответствии с правилами видимости, которые будут объяснены в следую- [ щем разделе данной главы.
Это может звучать как тавтология, но вычисление выражения t r u e дает значение true . Таким рбразом, w h i l e (true) представляет собой бесконечный цикл, и от зацикливания спасает только наличие оператора break в теле цикла. При исполь зовании цикла w h i l e (true) никогда не забывайте об операторе break, который должен прервать работу цикла по достижении заданного условия.
Вот как выглядит образец вывода программы:
Введите сумму вклада : - 1 0 0 0 Вклад не может быть отрицателен Повторите ввод
102 |
Часть II. Основы программирования в С# |
Введите |
сумму вклада :10 0 |
0 |
Введите |
процентную ставку |
: - 1 0 |
Процентная ставка не может быть отрицательна или превышать 50 Повторите ввод
Введите |
процентную |
ставку:1 0 |
Введите |
количество |
л е т : 5 |
Вклад |
= 10 0 0 |
|
Проценты = 10% |
|
|
Срок , |
= 5 y e a r s |
|
1-1100 . 0
2-1210.00
3-1331.00
4-1464.10
5-1610 . 51
Нажмите <Enter> для завершения программы...
Программа отказывается принимать отрицательные значения вклада и процентов, по зволяя пользователю исправить ошибку ввода.
Всегда поясняйте пользователю, в чем именно он не прав, перед тем как дать ему возможность исправить допущенную ошибку. При ошибках, связанных с форматированием, неплохой идеей будет демонстрация примера корректного ввода. И будьте очень вежливы в своих сообщениях!
Правила о б л а с т и в и д и м о с т и
Переменная, объявленная в теле цикла, определена только внутри этого цикла. Рас смотрим следующий фрагмент исходного текста:
int |
nDays = 1; |
|
while(nDays < nDuration ) |
||
{ |
|
|
|
i n t |
nAverage = nValue / nDays; |
|
/ / |
. . . Некоторая последовательность операторов . . . |
i |
nDays = nDays + 1; |
Переменная nAverage не определена вне цикла w h i l e . Тому есть целый ряд при чин, но рассмотрим одну из них: при первом выполнении цикла программа встречает объявление i n t nAverage. При втором выполнении цикла то же объявление встреча ется еще раз, так что если бы не было правила области видимости переменной, это при вело бы к ошибке, так как переменная была бы уже определена. .
Можно привести и другие, более убедительные причины правилу области ви димости, но пока должно хватить и приведенного аргумента.
Достаточно сказать, что переменная nAverage прекращает свое существование при достижении программой закрывающей фигурной скобки и вновь создается при каждом выполнении тела цикла.
(пава 5. Управление потоком выполнения |
103 |
Опытные программисты говорят, что область видимости переменной nAver-l age ограничена циклом w h i l e .
Несмотря на свою простоту, цикл w h i l e все же является вторым по распространен ности циклом в программах на С#. Пальму первенства прочно удерживает цикл foil имеющий следующую структуру:
for{Выражение 1; |
Условие; |
Выражение2) |
{ |
|
|
I I . . . тело |
цикла . . . |
|
1 |
|
|
По достижении |
цикла f o r |
программа сначала выполняет Выражение!. Затем он |
вычисляет Условие, и если оно истинно, она выполняет тело цикла, которое заключен! в фигурные скобки и следует сразу после оператора for . По достижении закрывающей
скобки управление |
переходит Выражению2, после чего вновь вычисляется Условия |
|
и цикл повторяется. |
|
|
Определение цикла f o r можно переписать как следующий цикл w h i l e : |
||
Выражение 1 |
; |
|
whi1е( У с л о в и е ) |
|
|
{ |
|
|
I I . . . |
тело |
цикла . . . |
Выражение2;
}
П р и м е р
Возможно, вы лучше разберетесь, как работает цикл for, взглянув на конкретны! пример:
// Некоторое выражение на С#
а= 1;
//Цикл
f o r ( i n t nYear = 1; nYear < n D u r a t i o n ; nYear = nYear + 1)
{
/ / . . . тело цикла . . .
}
// |
Здесь программа продолжается |
а |
= 2 ; |
|
Предположим, программа выполнила присваивание а = 1 ; . После этого она объявляя |
переменную nYear и инициализирует ее значением 1. Далее программа сравнивает зна чение nYear со значением nDuration . Если nYear меньше nDuration, выполняется тело цикла в фигурных скобках. По достижении закрывающей скобки программа во}| вращается к началу цикла и выполняет инструкцию nYear=nYear+l, перед тем кап вновь перейти к проверке условия nYear<nDuration .
104 |
Часть II. Основы программирования в & |