Цикъл (програмиране)

от Уикипедия, свободната енциклопедия
Вижте пояснителната страница за други значения на Цикъл.

Цикличният изчислителен процес е процес, за по-накратко наричан цикъл, който представлява многократното изпълнение на дадена последователност от операции с различни данни. Най-често се променя само една величина, която се нарича параметър на цикъла. При различните видове цикли програмният код се повтаря, докато е в сила определено предварително зададено условие или фиксиран брой пъти, които са упоменати в началото. Всеки цикличен процес се характеризира със следните елементи:

  1. Инициализация – задава се началната стойност на параметъра на цикъла.
  2. Тяло на цикъла – инициализира се кодът, който трябва да се изпълни определен брой пъти.
  3. Актуализация – обновява се стойността на параметъра на цикъла.
  4. Прекъсващо условие – изразът, в зависимост от чиято стойност цикълът спира или продължава действието си.

Пропускането или неправилното задаване на някой от елементите на цикъла, може да доведе до грешка в изпълнението му или невъзможност за изпълнение.

В зависимост от мястото на прекъсващото условие, преди тялото на цикъла или след него, циклите се делят на цикли с предусловие и цикли със следусловие.

For цикъл[редактиране | редактиране на кода]

Наименованието for произхожда от английската дума for – в превод на български – „за“, защото по време на изпълнението си тялото на цикъла се изпълнява за дадена променлива в самия цикъл. Думата for се използва като ключова в повечето езици за програмиране.

В компютърната наука for циклите са блокове от програмен код, които могат да се изпълняват, повтаряйки операциите, вписани в тях.

За разлика от останалите типове цикли, в структурата на for циклите се въвежда брояч, чрез който може да се контролира броя на извършваните итерации. For циклите са приложими, когато предварително е установено колко на брой итерации трябва да извърши дадената програма.

Структура[редактиране | редактиране на кода]

Структурата на for циклите включва:

а) инициализационен блок
б) условие на цикъла
в) команди за обновяване на водещите променливи
г) Тяло на цикъла

Пример за представяне структурата на for цикъл в езика C#:

for (инициализация-а; условие-б; обновяване на водещата променлива-в)
{
    Тяло на цикъла  г
}

Инициализация[редактиране | редактиране на кода]

For циклите могат да имат инициализационен блок. Той се изпълнява само веднъж, точно преди влизане в цикъла и не се изпълнява повече, по време на работа на програмата. Инициализационият блок се използва за деклариране на водеща променлива – наричана още брояч и задаване на нейна начална стойност. Тази променлива може да се използва само в рамките на цикъла. Съществуват цикли, при които в инициализационния блок се декларира и инициализира повече от една променлива. Броячът на for цикъла го отличава от останалите видове цикли.

Съществува възможност водещата променлива да се декларира и инициализира извън цикъла. Тогава след изпълнението на цикъла тя ще запази последната си стойност, след последната итерация.

Един for цикъл може да има една или няколко водещи променливи, които се движат в нарастващ, намаляващ ред или с някаква стъпка, както и възможност едната променлива да се увеличава като стойност, а другата – да намалява.

Пример за инициализиране на променлива във for цикъл, в езика C#:

for (int number = 1; ; )
{
    // Променливата number е видима тук и може да се използва само тук.
}

Условие[редактиране | редактиране на кода]

Условието за повторение, или наричано още условие за прекратяване (на английски: loop condition) на цикъла, e булев израз, който се изпълнява веднъж, преди всяка итерация на цикъла, точно както при while циклите. Чрез него се определя в кой точно момент при коя по ред итерация да се прекрати изпълнението на цикъла. В случай, че условието има резултат true (истина) се изпълнява тялото на цикъла, а при false (неистина) то се пропуска, цикълът завършва и се преминава към останалата част от програмата, намираща се след цикъла.

Условието на цикъла винаги е булев израз. В случаите когато не е въведено условие за прекратяване на цикъла, тогава ще се изпълняват безкраен брой итерации.

Пример за представяне на условие във for цикъл, в езика C#:

for (int number = 1; number < 20; )
{
    Tяло на цикъла;
}

Обновяване на водещата променлива[редактиране | редактиране на кода]

Изпълнява след всяка итерация на цикъла, след като е приключило изпълнението на програмата в тялото на цикъла. Използва за обновяване стойността на водещата променлива, чрез което се упражнява контрол върху броя на оставащите итерации (повторения). След това се проверява дали условието на цикъла е валидно или не.

Пример за обновяване на променлива във for цикъл, в езика C#:

for (int number = 0; number < 20; number++)
{
    Tяло на цикъла;
}

Тяло на цикъла[редактиране | редактиране на кода]

Тялото на цикъла съдържа същинската част на програмата – блок със сорс код (наричан още изходен код или програмен код). В него са достъпни променливите, декларирани в инициализациония блок на цикъла. Този код се изпълнява при всяка следваща итерация (повторение), докато условието за прекратяване на цикъла бъде изпълнено и се излезе от цикъла.

Примери[редактиране | редактиране на кода]

Примери за реализирането на for цикъл в езика за програмиране C#:

for (int i = 2; i <= 20; i += 2)
{
    Console.Write(i + " "); //При всяка итерация, се разпечатва следващото по ред четно число + един интервал.
}
// Резултат: 2 4 6 8 10 12 14 16 18 20

В посочения пример се извършват 10 итерации във for цикъл. Преди всяка една от тях, се проверява дали е изпълнено условието за прекратяване на цикъла – стойността на променливата i, да е по-голяма от 20. До момента в който не е изпълнено това условие – променливата i да има стойност по-малка или равна на 20, се разпечатват всички четни числа които са по-големи от 1 и по-малки или равни на 20.

Примери за реализирането на for цикъл в езика за програмиране C++:

for (int i= 5; i<= 50; i += 2){
     printf("%d",i);
    }

Аналогичен пример, при който се разпечатват четните числа от 6 до 50.

Примери за for цикъл в други програмни езици:

ActionScript 3

for (var counter:uint = 1; counter <= 5; counter++){
  //statement;
}

Ada

for Counter in 1 .. 5 loop
   -- statements
end loop;

AppleScript

repeat with i from 1 to 5
	-- statements
	log i
end repeat

Bash

# first form
for i in 1 2 3 4 5
do
    # must have at least one command in loop
    echo $i  # just print value of i
done
# second form
for ((i = 1; i <= 5; i++))
do
    # must have at least one command in loop
    echo $i  # just print value of i
done

Basic

For I = 1 to 5;
 Print I;
Next I

C

for (initialization; condition; increment-decrement) {
    // Statements.
}

Fortran

do counter = 1, 5, 1
  write(*, '(i2)') counter
end do

Haskell

forM_ [1..5] $ \indx -> do statements
statements_result_list <- forM [1..5] $ \indx -> do statements

Java

for(int i = 0; i < 5; i++) {
    //perform functions within the loop;
    //can use the statement 'break;' to exit early;
}

JavaScript

for (var i = 0; i < 5; i++) {
    // ...
}

Lua

for i = start, stop, interval do
     -- statements
end
for i = 1, 5, 2 do
     print(i)
end
for name, phone, address in contacts() do
     -- contacts() must be an iterator function
end

Mathematica

Do[f[x], {x, 0, 1, 0.1}]
For[x=0, x <= 1, x += 0.1,
    f[x]
]

Matlab

for i = 1:5
     -- statements
end

Maxima CAS

for x:0.5 step 0.1 thru 0.9 do
    /* "Do something with x" */

Oberon-2, Oberon-07, or Component Pascal

FOR Counter := 1 TO 5 DO
  (* statement sequence *)
END

OCaml

 (* for_statement := "for" ident '='  expr  ("to" ∣  "downto") expr "do" expr "done" *)

for i = 1 to 5 do
    (* statements *)
  done;;

for j = 5 downto 0 do
    (* statements *)
  done;;

Pascal

for Counter := 1 to 5 do
  (*statement*);

Perl

for ($counter = 1; $counter <= 5; $counter++) { # implictly or predefined variable
  # statements;
}
for (my $counter = 1; $counter <= 5; $counter++) { # variable private to the loop
  # statements;
}
for (1..5) { # variable impicitly called $_; 1..5 creates a list of these 5 elements
  # statements;
}
statement for 1..5; # almost same (only 1 statement) with natural language order
for my $counter (1..5) { # variable private to the loop
  # statements;
}

PHP

for ($i = 0; $i < 5; $i++) {
  # statements;
}

PL/I

do counter = 1 to 5 by 1; /* "by 1" is the default if not specified */
  /*statements*/;
  end;

PostScript

5 { STATEMENTS } repeat

Python

for counter in range(1, 6): # range(1, 6) gives values from 1 to 5 inclusive (but not 6)
  # statements

Ruby

for counter in 1..5
  # statements
end

5.times do |counter| # counter iterates from 0 to 4
  # statements
end

1.upto(5) do |counter|
  # statements
end

Smalltalk

1 to: 5 do: [ :counter | "statements" ]

For цикъл с повече от една променлива[редактиране | редактиране на кода]

За по-бързото и практично разрешаване на някои проблеми е нужна повече от една променлива. Понякога в работата е трябва да се използва допълнителна променлива в инициализацията на for цикъл, която да се променя с въртенето на цикъла.

Пример за for цикъл с повече от една променлива в C#

        for (int i = 0, j = 2; i<5; i++, j*=10)
        {
            Console.WriteLine(j);
        }

//резултат
//2
//20
//200
//2000
//20000

Това, което трябва да се направи при декларирането на променливите в условието на for цикъла е да се зададе тяхната начална стойност и как ще се променят след всяко въртене. Условието за край на цикъла може да е обвързано само с едната или дори и с двете променливи едновременно. Когато то е достигнато това ще прекъсне работата на целия цикъл

Пример за for цикъл с повече от една променлива в C++

{
   int i, j;
   for (i = 5, j = 10; i + j < 20; i++, j++)
      printf_s("\n i + j = %d", (i + j));
}

While и do-while цикли[редактиране | редактиране на кода]

Цикълът while[редактиране | редактиране на кода]

While (на български докато) цикълът е цикъл, който се изпълнява докато предварително зададено булево условие в началото на цикъла е истина.

Структура на while цикъл

Цикълът работи по следния начин:

  1. Проверява се условието и ако то е истина се продължава към кода в тялото на цикъла. В противен случай цикълът приключва.
  2. Изпълнява се кодът в тялото на цикъла.
  3. Отново се проверява условието на цикъла и т.н.

Тялото на цикъла трябва да бъде заградено в къдрави скоби({тяло на цикъла}), а условието в обикновени((условие)).

Пример за while цикъл в C#

int i = 0;
 while (i<10) //условие
  {
    Console.Write(i); //тяло на цикъла
    i++;
  }
//резултат 0123456789

Зададена е променлива i, която е равна на 0. Условието на цикъла е да работи докато i е по-малка от 10. С всяко завъртане на цикъла i се увеличава с 1. В противен случай цикълът би бил безкраен и ще отпечатва 0 на екрана всеки път. При последното завъртане на цикъла, той отпечатва 9 на конзолата и увеличава стойността на i с единица. Вече i е рано на 10. При проверката на условието в началото на цикъла то вече е лъжа (false), защото i не е по-малко от 10, цикълът прекъсва работата си.

Пример за while цикъл в C++

int i = 0;
while(counter < 5) # условие
      {
          cout << "i: " << i << "\n";   # тяло на цикъла
          i++;
     }

Do-while цикъл[редактиране | редактиране на кода]

Do-while конструкцията е подобна while цикъла, но с тази разлика, че условието е зададено в края т.е. проверката се прави след като се изпълни тялото на цикъла.

Пример за do-while цикъл в C#

int i = 0;
           do
	{
  	    Console.Write(i);    //тяло на цикъла
 i++;
	}
               while (i < 10)    //условие

Пример за while цикъл в C++

int i = 0;
do
      {
          cout << "i: " << i << "\n";    #тяло на цикъла
          i++;
     }
 while(counter < 5)     #условие

Примерите са аналогични на дадените примери за while цикъл и резултатът от тях ще бъде индентичен. While цикълът е този, който се използва по-често в практиката в сравнение с do-while.

Примери[редактиране | редактиране на кода]

ActionScript 3

var counter:int = 5;
var factorial:int = 1;

while (counter > 1)
{
  factorial *= counter;
  counter--;
}
trace ("Factorial ", factorial);

Ada

with Ada.Integer_Text_IO;

procedure Factorial is
  Counter   : Integer := 5;
  Factorial : Integer := 1;
begin
  while Counter > 0 loop
    Factorial := Factorial * Counter;
    Counter   := Counter  1;
  end loop;

  Ada.Integer_Text_IO.Put (Factorial);
end Factorial;

Bash

counter=5
factorial=1
while [ $counter -gt 0 ]; do
    factorial=$((factorial * counter))
    counter=$((counter  1))
done

echo $factorial

Basic

    Dim counter As Integer = 10    ' init variable and set value

    Do While counter > 0
          counter = counter  1
    Loop     ' program goes here, until counter = 0

C or C++

int main (void)
{
  int counter = 5;
  long factorial = 1;

  while (counter > 1)
  {
     factorial *= counter--;
  }
  printf(factorial);
  return 0;
}

Fortran

program FactorialProg
  integer :: counter = 5
  integer :: factorial = 1
  do while (counter > 0)
    factorial = factorial * counter
    counter = counter  1
  end do
  print *, factorial
end program FactorialProg

Java, C#, D

int counter = 5;
long factorial = 1;

while (counter > 1)
{
   factorial *= counter--;
}

JavaScript

var counter = 5;
var factorial = 1;

while (counter > 1)
{
  factorial *= counter--;
}

document.write(factorial);

Lua

counter = 5
factorial = 1

while counter > 0 do
  factorial = factorial * counter
  counter = counter  1
end

print(factorial)

Matlab

counter = 5;
factorial = 1;

while (counter > 0)
  factorial = factorial * counter;      %Multiply
  counter = counter  1;                %Decrement
end

factorial

Mathematica

Block[{counter=5,factorial=1}, (*localize counter and factorial*)
        While[counter>0, (*While loop*)
               factorial*=counter; (*Multiply*)
               counter--; (*Decrement*)
             ];
     factorial
    ]

Oberon, Oberon-2, Oberon-07, Component Pascal

MODULE Factorial;
IMPORT Out;
VAR
  Counter, Factorial: INTEGER;
BEGIN
  Counter := 5;
  Factorial := 1;
  WHILE Counter > 0 DO
    Factorial := Factorial * Counter;
    DEC(Counter)
  END;
  Out.Int(Factorial,0)
END Factorial.

Maya Embedded Language

int $counter = 5;
int $factorial = 1;

int $multiplication;

while ($counter > 0)
{
    $multiplication = ($factorial * $counter);

    $counter -= 1;

    print ("Counter is: " + $counter + ", multiplication is: " + $multiplication + "\n");
}

Pascal

program Factorial1;
var
  Counter, Factorial: integer;
begin
  Counter := 5;
  Factorial := 1;
  while Counter > 0 do
  begin
    Factorial := Factorial * Counter;
    Counter := Counter  1
  end;
  WriteLn(Factorial)
end.

Perl

my $counter = 5;
my $factorial = 1;

while ($counter > 0) {
    $factorial *= $counter--; # Multiply, then decrement
}

print $factorial;
open IN, "<test.txt";
while (<IN>) {
  print;
}
close IN;

PHP

$counter = 5;
$factorial = 1;
while($counter > 0) {
  $factorial *= $counter; // Multiply first.
  $counter--; // then decrement.
}
print $factorial;

PL/I

declare counter fixed initial(5);
declare factorial fixed initial(1);

do while(counter > 0)
  factorial = factorial * counter;
  counter = counter  1;
  end;

Python

counter = 5 # Set the value to 5
factorial = 1 # Set the value to 1

while counter > 0: # While counter(5) is greater than 0
      factorial *= counter              # Set new value of factorial to
                                        # factorial x counter.

      counter -= 1                      # Set the new value of counter to
                                        # counter – 1.

print factorial # Print the value of factorial.

Racket

#lang racket
(define counter 5)
(define factorial 1)
(let loop
  (when (> counter 0)
    (set! factorial (* factorial counter))
    (set! counter (sub1 counter))
    (loop)))
(displayln factorial)

Ruby

# Calculate the factorial of 5
i = 1
factorial = 1
while i < 5
  factorial *= i
  i += 1
end
puts factorial

Smalltalk

| count factorial |
count := 5.
factorial := 1.
[ count > 0 ] whileTrue:
    [ factorial := factorial * count.
    count := count  1 ].
Transcript show: factorial

Tool command language

set counter 5
set factorial 1

while {$counter > 0} {
  set factorial [expr $factorial * $counter]
  incr counter -1
}

puts $factorial

Foreach[редактиране | редактиране на кода]

Foreach е идиом от езиците за програмиране за основно разглеждане на предмети в дадена колекция. За разлика от другите видове цикли, foreach циклите нямат определено ограничение на броя на повторенията на цикъла. Идеята е да се направи определено действие за всеки един от дадените елементи, а не да извърша дадено действие n-брой пъти. Използването на foreach цикли улеснява четимостта на кода. Foreach цикъл не позволява промяна на променливите, които са зададени в инициализацията на цикъла (по време на работата на цикъла те са константни и не могат да бъдат променяни), а просто ги обхожда. Винаги се започва от елемента намиращ се нулевата позиция, като на всяка следваща итерация се взима елемент с позиция по-голяма с единица докато кодът не се изпълни за всеки един елемент съдържащ се в инициализацията на цикъла.

C#[редактиране | редактиране на кода]

string str = "Hello";
foreach (char c in str)
{
  Console.WriteLine(c);
}

//Резултат:
//H
//e
//l
//l
//o

Низът бива разглеждан като съвкупност от елементи и foreach цикълът обхожда всеки един него елемент.

C++[редактиране | редактиране на кода]

В C++ синтаксисът на Foreach циклите е взет от Java.

#include <QList>
#include <QDebug>
int main()
{
  QList<int> list;

  list << 1 << 2 << 3 << 4 << 5;

  foreach (int i, list)
  {
    qDebug() << i;
  }
}

Забелжка: Настоящият синтаксис е нововъведене от стадарта C++ 11.

Вложени цикли[редактиране | редактиране на кода]

Ако в тялото на един цикъл се включи нова инструкция за цикъл, се получава по-сложна конструкция, наречена „цикъл в цикъл“ или вложени цикли. В този случай цикълът, който се намира в първия се нарича вътрешен, а първият цикъл външен. Всеки от двата цикъла се характеризира четирите основни елемента – инициализация, тяло, прекъсващо условие и актуализация. Задължително е параметрите на двата цикъла да са различни променливи т.е. да са именувани по различен начин. При всяко едно изпълнение на действията във външния цикъл се извършва пълен брой изпълнения на действията във вътрешния цикъл.

Пример за вложени цикли в C#

for (int i = 0; i < 3; i++)
{
     for (int j = 0; j < 5; j++)
        {
              Console.Write(j);
         }
     Console.WriteLine();
}
//резултат
//01234
//01234
//01234

За всяка една стойност на i от външния цикъл, вътрешният се изпълнява изцяло т.е. извършва пълен брой завъртания. При всяко едно ново стартиране на вътрешния цикъл (за всяка промяна на стойността на външния цикъл) той започва инициализацията с параметрите, които са му зададени от самото начало освен, ако някой от тях не е обвързан с дадена променлива, която се използва във външния цикъл.

Пример за вложени цикли в C++

for(num2 = 0; num2 <= 9; num2++)
{
      for(num1 = 0; num1 <= 9; num1++)
      {
            cout<< num2<< "   " << num1<< endl;
      }
}

Могат да бъдат използвани различни комбинации от цикли за съставянето на вложени цикли, както и да се ползват повече от два цикъла

Безкраен цикъл[редактиране | редактиране на кода]

Цикъл, който никога не завършва, се нарича безкраен цикъл (на английски: infinite loop). Безкраен цикъл е поредица от команди в компютърна програма, която извършва безкраен брой итерации. Безкрайните цикли може да възникнат при for, while, do-while циклите. Причините за възникването им може да са:

  • Дължащи се на липсата на условие за прекратяване изпълнението на програма.
  • Въведено е условие за прекратяване действието на цикъла, което никога не може да бъде изпълнено.
  • Въведено е условие за прекратяване действието на цикъла, след изпълнението на което, цикълът започва работа от началната си точка.

Приложение[редактиране | редактиране на кода]

Съвременните компютри[редактиране | редактиране на кода]

Компютрите постоянно следят дали потребителят е въвел определени символи чрез клавиатурата си или използва входно-изходни устройства, чрез които да извършва определени действия – микрофон, слушалки, видеокамера, принтери и други. За тази цел компютърните системи използват безкрайни цикли които прекъсват, тогава когато потребителят извърши дадено действие – използване на клавиатура, подаване на команда за принтиране на страница и други. Тези цикли работят дотогава докато компютърът не бъде изключен или рестартиран.

Приложения 24/7[редактиране | редактиране на кода]

Използват се в приложения, които работят денонощно – банкомати, уеб приложения, специфичен софтуер в болнични и военни заведения. При всички тях, след изпълнението на определена команда, безкрайният цикъл се прекъсва, за да се извърши точно определена операция. Пример – теглене на парични средства от банкомат. След като потребителят извърши операцията и банкоматът предостави исканата сума, машината се връща в основното си положение – изчаква подаването на нова заявка за теглене на средства – безкраен процес, докато друг потребител не използва машината и не прекъсне цикъла.

Стрес тестове[редактиране | редактиране на кода]

Безкрайните цикли могат да бъдат използвани и за стрес тестове на софтуерни приложения или хардуер. Стрес тестовете представляват проверка работоспособността на дадено приложение при голяма натовареност, с цел да се провери къде е уязвим даденият софтуер или хардуер. Чрез един безкраен цикъл може да се провери дали приложението ще работи при реализирането на неограничен брой итерации, с цел симулация при негово максимално натоварване в реална среда.

Псевдо-безкраен цикъл[редактиране | редактиране на кода]

Невъзможност за достигане до условието за прекратяване на цикъла[редактиране | редактиране на кода]

Представляват цикли които според начина си на дефиниране, на пръв поглед изглеждат безкрайни. Получават наименованието си, предвид това, че за да бъде приключена работата им е необходимо извършването на огромен брой итерации. Причина за това е фактът, че променливата i, след като достигне максималната си стойност, ще бъде препълнена – ще се достигне до нейната максимална стойност. При добавянето на единица, при следващото завъртане на цикъла, променливата i ще получи стойност 0, с което цикълът ще приключи. За целта са необходими огромен брой итерации, които на практика не могат да бъдат реализирани.

Пример за безкраен цикъл, с невъзможност за достигане условието за прекратяване в езика C#:

for (uint = 1; i > 0; i++)
{
    Тяло на цикъла.
}

В посочения пример е въведена променлива i, със стойност 1. При всяка следваща итерация, стойността на променливата се увеличава с 1. Това ще продължи докато не бъде достигната максималната стойност за съответния тип данни. В конкретния случай, максималната стойност на типа данни uint e 4 294 967 295. Когато се достигне тази стойност и се добави отново 1, променливата се препълва и започва отброяване отново от нейната минимална стойност. В този случай – 0. Тогава ще се прекрати работата на цикъла, защото е достигнато условието, при което променливата i e по-малка от 1. За да се случи това, е необходимо да се реализират 4 294 967 295 итерации.

Алдерсонов цикъл и безкрайна рекурсия[редактиране | редактиране на кода]

Представлява безкраен цикъл, в който е заложено условие за изход от цикъла, но най-често поради програмна грешка, изпълнението на програмата не може да достигне до условието за прекратяване изпълнението на цикъла.

Безкрайната рекурсия е особен вид безкраен цикъл при който се използва рекурсия и след всяка итерация, цикълът започва от началната си точка, без да съществува възможност за изход от него.

Пример за безкраен цикъл в езика C#:

while (true)//Въведено е условие, което винаги ще бъде валидно.
{
     Console.Write("Този цикъл ще извършва безкраен брой итерации.");
}

В цикъла е въведено условие което винаги ще бъде вярно. В тялото на цикъла няма въведена конструкция, която да промени валидността на условието. Поради тази причина, този цикъл ще изпълнява безкраен брой пъти.

Пример за безкраен цикъл в езика C++:

for(;;)//Не е въведено условие, след изпълнението на което, цикълът да прекрати работата си.
{
    printf("Този цикъл ще извършва безкраен брой итерации.");
}

Разлика във видовете цикли[редактиране | редактиране на кода]

Конструкция, употреба и скорост на работа[редактиране | редактиране на кода]

Всеки цикъл изпълнява различна функция и разрешава конкретен тип задачи. Поради тази причина циклите се различават от гледна точна на конструкцията, поведението си и скорост на работа. Въпреки това, всички типове цикли имат условие, което ако не бъде изпълнено, приключва работата на цикъла, както и тяло, в което е поместен кодът, който се изпълнява докато условието за приключване на цикъла е в сила.

For циклите – използват се тогава, когато предварително е ясен броят на итерациите, които трябва да се извършват, чрез предварително задаване на условие (брояч), което ще се изпълни след определения брой превъртания на цикъла. Именно условието, с което се определят броя на извършваните итерации от for цикъла го отличава от всички останали.

While циклите – за разлика от for циклите, при while конструкциите не се въвежда брояч на итерациите, а само и единствено условие след изпълнението на което, цикълът се прекратява. Това е основната разлика спрямо for циклите. Скоростта им на работа е приблизително еднаква.

Do – while циклите – също като while циклите не използват брояч на итерациите. Разликата спрямо останалите типове цикли се състои в това, че конструкцията на цикъла е такава, че винаги се извършва една итерация, след което се проверява дали условието за приключване на цикъла е валидно или не е. Скоростта на работа на този тип цикли не се различава от for и while циклите. Удобни за употреба, когато се налага да се въвеждат входни данни от потребителя, които трябва да бъдат валидирани.

Foreach циклите – използват се основно за обхождането на масив от елементи. Разликите спрямо останалите цикли са в това, че условието за изход е достигането на последен елемент от даден масив. Не представлява булев израз какъвто се използва при останалите типове цикли. Скоростта на работа на foreach циклите е приблизително еднаква спрямо тази на останалите, но работят приблизително два пъти по-бавно, когато обхождат динамично-разширяемите масиви (на английски: Dynamic array или Array List). Въпреки че при стандартни операции работят с висока скорост им е необходимо заделянето на по-голяма памет, защото използват повече на брой променливи, спрямо извършването на същите операции при for циклите. Foreach циклите могат да обхождат елементи от масив само в една посока – от елемент с най-малък индекс, към елемента с най-голям индекс. При for циклите тази операция може да се извършва в двете посоки.

Оператор Goto[редактиране | редактиране на кода]

Goto е оператор, срещан в много от програмните езици. Той изпълнява еднопосочно препращане до определен ред код. Местата на препратка обикновено се идентифицират с използването на етикети (на английски: labels), но някои от езиците изискват номер на реда за препратка. Goto не играе важна роля в написването на програма и поради тази причина може да бъде пропуснато. Съществуват други термини, заменители на goto с идентични функции.

Употреба[редактиране | редактиране на кода]

Често се използва с if твърдения за да създаде условие за препратка.

IF condition THEN goto label

Критика[редактиране | редактиране на кода]

През 1970-те и 1980-те години употребата на goto рязко намалява поради въвеждането на структурно програмиране, целящо да подобри качеството на писане и четимостта на кода. Смята се че използването на goto води до така наречения „спагети код“.

Има и друго твърдение, на Доналд Кнут, който анализира много често срещани програмни задачи и доказва, че употребата на goto е оптималният вариант за решение. Известни Линукс програмисти като Кърнел, Линус Торвалдс и Стийв МакКонъл също твърдят, че goto е полезен инструмент и може да подобри скоростта на изпълнение на програмата, размера и четимостта на кода, но само ако се използва по разумен начин от опитни програмисти.

Оператори break и continue[редактиране | редактиране на кода]

Оператор break[редактиране | редактиране на кода]

Операторът break (на български прекъсвам) се използва за преждевременно излизане от цикъл, преди той да е завършил изпълнението си по естествения си начин. При срещане на оператора break цикълът се прекратява и изпълнението на програмата продължава от следващия ред веднага след тялото на цикъла. Прекратяването на цикъл с оператора break може да стане само от неговото тяло, когато то се изпълнява в поредната итерация на цикъла. Когато break се изпълни, кодът след него в тялото на цикъла се прескача и не се изпълнява.

Цел на break конструкцията е крайната точка на най-близкия за изпълнение цикъл. В случаите когато операторът break се използва в цикъл, който е вложен в друг, при срещане на оператора, се прекратява действието само на вложения цикъл и се преминава към следваща итерация на главния цикъл.

Конструкцията break се използва във всички типове цикли – for, while, foreach, do – while, както и при switch конструкции.

Пример за работа с break оператор в езика C#:

for (int i = 1; i <= 10; i++)
{
    if (i == 5)
    {
        break; //Операторът ще приключи работата на цикъла, при i = 5;
    }

    Console.Write(i + " ");
}

//Result: 1 2 3 4

Примерът демонстрира работата на for цикъл, при който се разпечатват числата от 1 до 4. Въведена е променлива i с първоначална стойност 1. При всяка следваща итерация, нейната стойност се увеличава с 1. В тялото на цикъла е зададено условие, ако променливата i, достигне стойност 5, да се активира оператор break, с който се прекъсва работата на цикъла и се излиза от него.

Пример за работа на оператор break в езика C++:

int i;

for (i = 1; i < 10; i++){
    printf_s("%d\n", i);

    if (i == 1){
    break;
    }
    // Цикълът ще приключи работа след първата итерация.

Оператор continue[редактиране | редактиране на кода]

Continue (на български продължавам), се използва, когато реализираният алгоритъм изисква пропускане на част от операторите или част от кода в тялото на даден цикъл.

Операторът continue връща изпълнението в началото на работещия цикъл, като кодът след него просто се прескача. Програмата пропуска част от кода – остатъкът от вложените оператори в същия цикъл за текущата итерация, симулирайки че е достигнат края на програмата и се преминава към следващата итерация – следващата стъпка на същия цикъл.

За разлика от break, който прекратява целия цикъл, continue прекратява само текущата итерация.

Пример за използване на оператор continue в езика C++:

    int i = 0;
    do
    {
        i++;
        printf_s("Този ред винаги ще се печата.\n");
        continue;
        printf("Този ред никога няма да се печата.\n");
     } while (i < 10);

Оптимизация на цикли[редактиране | редактиране на кода]

Оптимизирането на един цикъл може да бъде разглеждано като прилагането на поредица от преобразувания на сорс кода на цикъла или междинно представяне. Преобразуването (или серията от преобразувания) целят да запазят временната последователност от всички зависимости, за да се запази резултатът от програмата. Ползата от преобразуванията понякога се постига трудно, защото прилагането на едно преобразуване може да изисква преди това използването на друго/и, което в крайна сметка може да доведе до спад на производителността на програмата, вместо обратното.

Сливане на цикли[редактиране | редактиране на кода]

В компютърната наука сливането на цикли е операция с която се трансформират и оптимизират цикли. При нея два или повече цикъла се заместват и трансформират в един. Това е възможно когато двата цикъла оперират върху едни и същи данни.

Сливането на цикли не винаги подобрява скоростта на работа на дадената програма. Съществува операции, в някои компютърни архитектури, при които два цикъла могат да работят по-бързо отколкото може да работи само един. При тази ситуация с цел оптимизация, единият цикъл може да бъде разделен на два отделни. Тази операция се нарича делене на цикли.

Пример за работа на програма, с два цикъла, при която не е реализирана оптимизация – език C#:

ulong sum = 0;
ulong difference = 4999999950000000;

for (ulong i = 0; i <= 100000000; i++)
    {
        sum = sum + i;
    }

for (ulong i = 0; i <= 100000000; i++)
    {
        difference = difference  i;
    }

Чрез посочените примери се демонстрира времето за което програмата приключва работа, в случаите когато се използват два цикъла, след което се прилага оптимизация, от типа – сливане на цикли. Дефинирани са две променливи от типа ulong. Чрез първият цикъл се събират числата от 1 до 100 000 000. Чрез вторият се извърша аналогична операция, но в обратен ред – от въведената стойност – 49 999 999 500 000 00 (която представлява сумата на числата от 1 до 100 000 000), се изважда стойността на всяко едно от числата в диапазона от 100 000 000 до 1. Двете операции се извършват в два различни цикъла.

Използвани са големи стойности, с цел да се демонстрира разликата във времето, което е необходимо на програмите да обработят данните.

Пример за работа на програма, след извършена оптимизация – език C#:

for (ulong i = 0; i <= 100000000; i++)
    {
     sum = sum + i;
     difference = difference  i;
    }

Извършена е оптимизация – операциите извършвани в двата цикъла са събрани в един, предвид това, че и двата работят върху един и същ обхват от данни. Приблизителната разликата във времето което е необходимо за извършването на операциите, преди и след оптимизацията е:

- след оптимизацията: 0.997 сек.

- преди оптимизацията: 1.593 сек.

Разгръщане на цикъл (Loop unwinding)[редактиране | редактиране на кода]

Циклично преобразуване с цел подобрение на скоростта на изпълнение на програмата за сметка на двоичния и размер. Промяната може да бъде направена ръчно или с използване на оптимизатори. Целта на този метод е да увеличи скоростта на изпълнение на програмата като намали общият брой итерации на цикъла.

Пример[редактиране | редактиране на кода]

Normal loop After loop unrolling
 int x;
 for (x = 0; x < 100; x++)
 {
     delete(x);
 }
 int x;
 for (x = 0; x < 100; x+=5)
 {
     delete(x);
     delete(x+1);
     delete(x+2);
     delete(x+3);
     delete(x+4);
 }

Минуси[редактиране | редактиране на кода]

Увеличава обема на кода и го прави по-трудно четим. Освен малките и „по-прости“ команди, разгърнатите цикли (на английски: unrolled loops) които съдържат разклонения работят по-бавно и от рекурсия. Друг не по-малко важен недостатък на този похват е премахването на рестрикциите на цикъла. Това може да доведе до появата на грешки.

Плюсове[редактиране | редактиране на кода]

Увеличава скоростта на изпълнение на текущия цикъл.

Други често използвани преобразувания на цикли[редактиране | редактиране на кода]

  • fission/distribution – цикълът се разделя на няколко по-малки със същия обхват, но всеки от новите цикли работи върху само част от тялото.
  • fusion/combining – когато няколко свързани цикъла имат еднакъв брой итерации, телата могат да бъдат обединени в един.
  • interchange/permutation – при този метод на оптимизации вътрешните цикли се заменят с външни.
  • inversion – тази техника заменя while цикълът с do-while, който бива поставян в условна конструкция (if), като по този начин се намаляват с до 2 възможните завъртания. Така се дублира проверката (и леко се удължава кодът), но е по-ефективно и предпазва от образуването на „балон“ (pipeline stall).
  • loop-invariant code motion – ако има променлива, която се инициализира с всяко завъртане на цикъла, но нейната стойност е винаги една и съща, тя може да бъде зададена само веднъж извън тялото на цикъла.
  • parallelization – метод, който разбива и разпределя даден цикъл върху всички ядра на компютъра. Може да бъде направено автоматично от компилатора (automatic parallelization) или ръчно (с вмъкване на паралелни директиви като OpenMP).
  • reversal – променя редът, по който стойностите се записват към индексната променлива. По този начин е възможно да се елиминират различни зависимости, което да помогне за използването на други методи за оптимизация.
  • scheduling – разделя цикълът на няколко части, който могат да бъдат стартирани на няколко микропроцесора.
  • skewing – се използва при работата на вложени цикли върху многомерни масиви, където всяка итерация на вътрешния цикъл зависи от предишните итерации. Методът преподрежда достъпа до елементите по такъв начин, че единствената зависимост да е от итерациите на външния цикъл.
  • software pipelining – вид out-of-order изпълнение на итерациите на цикъла, което цели да компенсира забавяне на микропроцесора.
  • splitting/peeling – цели да опрости цикъла или да избегне различни зависимости. При този метод един цикъл се разделя на няколко цикъла със същото тяло, но които работят върху различна част от общия брой нужни итерации.
  • tiling/blocking – пренарежда цикъла по такъв начин, че да работи върху блокове от данни, които се побират в кеш паметта.
  • vectorization – цели да изпълни, колкото е възможно повече от итерациите едновременно на мултипроцесорна система.
  • unrolling – копиране няколко пъти тялото на цикъла с цел да се намалят итерациите и пътите, в които условието на цикъла ще бъде проверявано. Възможно е да се направи и пълно „развиване“ на цикъл, но трябва да се знае предварително броят итерации, които ще бъдат изпълнени.
  • unswitching – пренася условна конструкция, която е тялото на цикъла, извън него като копира тялото на цикъла и го поставя във всяка една от създадените условни конструкции.
  • sectioning – техника, която позволява SIMD кодиране на цикъла и подобрява използването на паметта.

Продуктивност на видовете цикли[редактиране | редактиране на кода]

Скоростта на различните цикли е доста близка и разликата се изразява в няколко наносекунди. Все пак при работа с огромен брой данни и променливи (например стотици милиони) тази разлика се натрупва, но въпреки това не може да стане прекалено осезаема. Натрупването не достига такива размери, които да забавят чувствително или навредят на работата на програмата. Лесно може да се направят тестове за скоростта на циклите на всеки един компютър като се използва Stopwatch Class от MSDN.

Вижте също[редактиране | редактиране на кода]

Източници[редактиране | редактиране на кода]