Сайт AZJIO
Главная | Регистрация | Вход Приветствую Вас Гость | RSS
Меню сайта
Категории раздела
Скрипты для LiveCD [0]
Скрипты для WindowsXP [8]
Примеры скриптов [15]
Функции [5]
Поиск
Главная » Статьи » Скрипты AutoIt3 » Примеры скриптов

Цикл

Цикл


Существуют 4 конструкции цикла

1. While...WEnd
2. Do...Until
3. For...To...Next
4. For...In...Next

While...WEnd и Do...Until почти одинаковы. Разница только в том что первый проверяет условие перед входом в цикл, второй после первого шага цикла.

Чаще всего While...WEnd и Do...Until делаются в виде бесконечного цикла, а по множественным условиям внутри цикла задаётся выход из него.

; Пример бесконечного цикла While...WEnd
While 1
    If MsgBox(4, 'Сообщение', 'Выйти из цикла') = 6 Then ExitLoop
WEnd


; Пример бесконечного цикла Do...Until
Do
    If MsgBox(4, 'Сообщение', 'Выйти из цикла') = 6 Then ExitLoop
Until
0


Особенность For...To...Next - возможность задать пределы цикла. Если первые 2 могут бесконечно работать, то For...To...Next совершает заданное количество шагов. Это полезно для обработки массивов, которые всегда имеют определённый размер, конкретное количество ячеек. В цикле обрабатывается каждая ячейка массива.

В цикле можно задать собственный счётчик итераций. Это часто необходимо в случаях перезаполнения массива валидными данными, отсеивая не валидные. Например:

#include <Array.au3>

Local $array[5] = [4, 'помидор', 'огурец', 'морковка', 'капуста'] ; Создаём массив
_ArrayDisplay($array, 'До цикла') ; Смотрим массив
$n = 0 ; Инициализируем счётчик
For $i = 1 To $array[0]
    If StringInStr($array[$i], 'к') Then ; Если ячейка массива содержит буквы "к", тогда
        $n += 1 ; Увеличиваем счётчик
        $array[$n] = $array[$i] ; Записываем текущий элемент массива в ячейку определяемую счётчиком
    EndIf
Next

$array[0] = $n ; Обновляем ячейку определяющую число элементов массива
ReDim $array[$n + 1] ; Обновляем размерность массива
_ArrayDisplay($array, 'После цикла') ; Смотрим массив


Если необходимо оставить исходный массив без изменения, то нужно создать второй массив того же размера и заполнять его. В качестве размера берётся размер исходного массива.

Local $array_dst[$array[0] + 1]


Циклы While...WEnd и Do...Until также можно сделать со счётчиком. Например:

; While...WEnd
Local $test
$i
= 0
While $i <= 5
    $test &= $i & @LF
    $i += 1
WEnd
MsgBox(0, 'Сообщение', $test)

; Do...Until
Local $test = ''
$i = 0
Do
    $test &= $i & @LF
    $i += 1
Until $i > 5
MsgBox(0, 'Сообщение', $test)


Цикл For...To...Next тоже можно сделать бесконечным влияя на счётчик, но этот случай не практичный.

$n = 0
For $i = 1 To 5
    $i -= 1
    $n += 1
    If $n = 10 Then ExitLoop
Next

MsgBox(0, 'Сообщение', '$n = ' & $n)


Чаще всего влияние на счётчик цикла For...To...Next является ошибкой программиста, из-за чего результат получается непредсказуемый (испорченный результат или зависание в цикле).

; Неправильно
$n = 0
$d = 0
For $i = 1 To 5
    $n += 1
    If $n = 1000 Then ExitLoop
    For $i = 0 To 2
        $d += 1
    Next
Next

MsgBox(0, 'Сообщение', '$n = ' & $n & @LF & '$d = ' & $d)

; Правильно
$n = 0
$d = 0
For $i = 1 To 5
    $n += 1
    If $n = 1000 Then ExitLoop
    For $j = 0 To 2 ; Имя счётчика $j отличается от $i
        $d += 1
    Next
Next

MsgBox(0, 'Сообщение', '$n = ' & $n & @LF & '$d = ' & $d)


Причина возникновения ошибки в следующем: Вложенный цикл заново инициализирует счётчик приравнивая его к 0. При выходе из цикла счётчик всегда на единицу больше, чем его верхний заказной предел. В данном случае если верхний предел итерации 2, то счётчик при завершении цикла равен 3. Далее родительский цикл увеличенает счётчик на 1 и он равен 4. Каждый последующий шаг цикла обнуляет счётчик и к началу шага родительского цикла делает его равным 4. Для завершения цикла счётчик должен быть равным 5, но этого никогда не происходит. Цикл зависает в бесконечности. Для выхода из цикла добавлен дополнительный независимый счётчик показывающий, что количество итераций превышает максимальное допустимое 5 * 3 = 15, которое равно произведению диапазонов цикла.


Цикл достаточно быстро выполняет операции, используя всю мощь процессора. Мой двух-ядерный процессор делает 4 миллиона итераций в секунду. Поэтому если вы мониторите какую либо величину, то следует делать паузу, чтобы ограничить число шагов цикла в течении определённого промежутка времени. Например:

While FileExists('C:\1.txt')
    Sleep(1000)
WEnd


Здесь осуществляется проверка существования файла, но запрос осуществляется 1 раз в секунду. Пауза ни в коем случае не должна восприниматься как обязательный атрибут цикла. В большинстве случаев важно максимально быстро выполнить. Но задача мониторинга - длительное время следить за объектом, который возможно изменится в течении часа и проверять объект слишком часто не имеет смысла. Задача программиста определить, на сколько быстро требуется реагировать на изменение и выбрать оптимальную задержку в цикле.
При обработке данных (массива), где итогом является получить результат как можно быстрее, вставка паузы ничего полезного не приносит.

Коротко о For...In...Next. Цикл в основном предназначен для работы с объектами. Для обработки массива часто не удобен из за отсутствия индекса обрабатываемого элемента. Но если индекс не нужен, то этот цикл даже проще, в том что не требуется получать/определять размер массива. Это всё происходит за ширмой самим интерпретатором, который вычисляет размер и возвращает в объявленную переменную значения элементов массива начиная от 0 на первом шаге цикла, заканчивая последним, на последнем шаге цикла.

Local $array[4] = ['помидор', 'огурец', 'капуста',  'морковка']
For $item In $array
    MsgBox(0, 'Сообщение', $item)
Next




Оптимизация цикла

Пример 1

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

$Trg = 1

; Не оптимизированно
For $i = 1 To 100000
    If $Trg Then
        $s = 1
    Else
        $s = 2
    EndIf
Next


; Оптимизированно
If $Trg Then
    For $i = 1 To 100000
            $s = 1
    Next
Else

    For $i = 1 To 100000
            $s = 2
    Next
EndIf


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


Пример 2

Оптимизация задачи с помощью двух последовательных циклов. В качестве примера используем предыдущий вариант обработки массива.

#include <Array.au3>

Local $array[5] = [4, 'помидор', 'огурец', 'капуста',  'морковка'] ; Создаём массив
_ArrayDisplay($array, 'До цикла') ; Смотрим массив
$n = $array[0]
For $i = 1 To $array[0]
    If Not StringInStr($array[$i], 'о') Then ; Если ячейка массива НЕ содержит буквы "о", тогда
        $n = $i ; Записываем счётчик для следующего диапазона обработки
        ExitLoop
    EndIf
Next
For
$i = $n + 1 To $array[0] ; $n + 1 - позиция, от которой требуется проверка данных
    If StringInStr($array[$i], 'о') Then ; Если ячейка массива содержит буквы "о", тогда
        $array[$n] = $array[$i] ; Записываем текущий элемент массива в ячейку определяемую счётчиком
        $n += 1 ; Увеличиваем счётчик
    EndIf
Next

$array[0] = $n - 1 ; Обновляем ячейку определяющую число элементов массива
ReDim $array[$n] ; Обновляем размерность массива
_ArrayDisplay($array, 'После цикла') ; Смотрим массив


Представим, что необходимо обрабатывать массив из нескольких тысяч ячеек, а по некоторым заведомо известным условиям начало сдвига ячеек в массиве приходится на любую часть массива в равной степени, т.е. в среднем как если бы это был центр массива. Если начать переписывать ячейки именно со средины, то это на значительный процент увеличило бы скорость алгоритма. Итак, в примере выше первый цикл делает поиск ячейки, от который необходимо производить сдвиг, а второй цикл собственно производит перезапись ячеек со сдвигом.

Пример 3

Вывести за пределы цикла все ресурсоёмкие функции, значения которых являются константами во время выполнения цикла.

; Не оптимизированно
For $i = 1 To $array[0]
    If Number(IniRead($Ini, 'section', 'key', '0')) > $array[$i] Then $array[$i] -= Number(IniRead($Ini, 'section', 'key', '0'))
    If BitAND(GUICtrlRead($iCh1),$GUI_CHECKED) Then $array[$i] -= 15
Next

; Оптимизированно
$value1 = Number(IniRead($Ini, 'section', 'key', '0'))
$value2 = BitAND(GUICtrlRead($iCh1),$GUI_CHECKED)
For $i = 1 To $array[0]
    If $value1 > $array[$i] Then $array[$i] -= $value1
    If $value2 Then $array[$i] -= 15
Next

В этом примере не оптимизированный вариант выполняет количество считываний ini-файла 18 раз (9 * 2), а считывание и проверка чекбокса 9 раз. Оптимизированный вариант получает значения вне цикла, а внутри цикла только манипулирует значениями содержащимися в переменных.

Категория: Примеры скриптов | Добавил: AZJIO (07.06.2014)
Просмотров: 6096 | Рейтинг: 5.0/1
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Вход
Друзья сайта
  • Справки AZJIO
  • USBTOR
  • PureBasic (Ru)
  • PureBasic (En)
  • AutoIT3 - Ru.Board
  • autoit-script.ru
  • autoitscript.com
  • WinPE - Ru.Board
  • Статистика

    Онлайн всего: 1
    Гостей: 1
    Пользователей: 0
    ЮMoney

    Мой кошелёк

    ЮMoney: 4100117604217624


    AZJIO © 2024