Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Books / 3_C#_2005_для_чайников_(Дэвис-2008)

.pdf
Скачиваний:
86
Добавлен:
24.03.2015
Размер:
15.46 Mб
Скачать

// Округляем

величину до копеек

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. Основы программирования в &

Переменная n Y e a r не определена вне области видимости цикла f o r , которая включает как тело цикла, так и его заголовок.

Зачем нужны разные ц и к л ы

Зачем в С# нужен цикл for, если в нем уже есть такой цикл, как w h i l e ? Наиболее простой и напрашивающийся ответ — он не нужен, так как цикл f o r не может сделать ничего такого, что нельзя было бы повторить с помощью цикла w h i l e .

Однако разделы цикла f o r повышают удобочитаемость исходных текстов, четко ука­ зывая три части, имеющиеся в каждом цикле: настройку, условие выхода и увеличение счетчика. Такой цикл не только проще для чтения и понимания, но и для проверки его корректности — вспомните, что основная ошибка при работе с циклом w h i l e — забы­ тое увеличение счетчика или некорректный критерий завершения цикла. Недаром цикл for встречается в программах на порядок чаще других разновидностей циклов.

Так уж сложилось, что в основном в первой части цикла f o r выполняется ини­ циализация переменной-счетчика, а в последней — ее увеличение. Но это не более чем традиция — С# не требует этого от программиста. В этих двух час­ тях цикла f o r можно выполнять какие угодно действия, хотя, конечно же, для того чтобы отойти от традиционной схемы, нужны серьезные основания.

В цикле f o r особенно часто используется оператор инкремента (который вместе с другими операторами был описан в главе 4, "Операторы"). Обычно приведенный ранее цикл for записывается как

for(int nYear = 1; nYear < n D u r a t i o n ; nYear++)

{

// . . . тело цикла .. .

}

Почти всегда в цикле f o r применяется постфиксная форма оператора инкре­ мента, хотя в данном случае функционально она идентична префиксной.4

У цикла f o r имеется одно правило, которое я не в состоянии пояснить — если усло­ вие в цикле отсутствует, считается, что оно равно t r u e . 5 Таким образом, f or (; ;) — такой же бесконечный цикл, как и w h i l e ( t r u e ) .

В реальных программах f o r (; ;) в качестве бесконечного цикла используется значительно чаще, чем w h i l e ( t r u e ) ; но чем это вызвано, объяснить сложно.

4К счастью, современные компиляторы достаточно интеллектуальны, чтобы понять, что в данном случае возвращаемое значение не играет никакой роли, и не выполнять дополнительной работы по сохранению начального значения переменной при использовании постфиксной формы оператора. — Примеч. ред.

5Это правило легко пояснить тем, что если бы отсутствие условия воспринималось как false, то в таком цикле выполнялась бы исключительно первая часть заголовка, но не последняя

ине тело цикла. — Примеч. ред.

Глава 5. Управление потоком выполнения

105