Swift - Как цикл for заканчивается до строки под ним?

Я разработчик-самоучка. Я никогда не учился в колледже и не посещал какие-либо курсы, на которых меня учили теории программирования или какой-либо из основополагающих концепций. Я просто знаю, как создавать приложения для iOS (с помощью книг, видео, встреч и практики), и я не знаю ничего из этого, поскольку Apple позаботилась о большей части этого, используя свой Xcode SDK (как и большинство компиляторов), поэтому Мне не нужно знать ничего из этого.

Хотя меня всегда смущало одно:

Если бы эти 2 оператора печати выполнялись, они печатались бы в правильном порядке сверху вниз (control flow)

print("I will print first")
print("I will print second")

Если запустить for-loop, он напечатает все числа в точном порядке, пока не будет выполнено условие:

for num in 1...10 {

    print(num)
    if num == 9 {
        print("done") // the for-loop has to iterate 9 times for it to print
        break
    }
}

Вот что меня смущает. Как получается, что если у меня есть долгое выполнение for-loop и оператор печати после него, цикл for завершается до того, как запустится оператор печати под ним?

for num in 1...10000000 {

    if num == 10000000 {
        print("why does this print") // the for-loop has to iterate 10 million times for it to print
    }
}

print("before this prints")

Цикл должен выполниться 10 миллионов раз, прежде чем этот оператор печати внутри него будет напечатан. Как получилось, что эти 10 миллионов итераций быстрее, чем просто печать этого "before this prints" оператора печати под ним?

Простите меня, если это вопрос, который, как предполагается, я должен знать, но ничего из того, что я когда-либо читал или смотрел, не касалось этого вопроса.

Дело не в том, чтобы быть Быстрее или помедленнее. Поскольку вы сами сказали что-то о поток управления, вы должны понимать, что здесь поток управления. Когда управление входит в цикл for, оно остается внутри цикла for после завершения/разрыва этого цикла. Все, что следует за этим циклом, будет выполнено после его завершения.

nayem 19.04.2019 13:33

@nayem Спасибо за ясность. Вы должны были опубликовать это как ответ. По сути, вы говорите, что он должен закончить то, что находится внутри цикла for, прежде чем он перейдет к следующей строке. Я никогда этого не знал. Я думал, что все просто потекло по порядку. Я не знаю, что цикл for на самом деле останавливает программу.

Lance Samaria 19.04.2019 13:37

Что ж, @Lance, я написал обширный ответ в дополнение к моему комментарию выше. Не торопитесь, чтобы прочитать это.

nayem 19.04.2019 16:28

@nayem Я проголосую за это, спасибо!

Lance Samaria 19.04.2019 16:31
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
1 629
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Ваш код выполняется последовательно.

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

DispatchQueue.global().async {
    for num in 1...10000000 {

        if num == 10000000 {
            print("why does this print") // the for-loop has to iterate 10 million times for it to print
        }
    }
}

print("before this prints")

Выход:

before this prints
why does this print

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

Lance Samaria 19.04.2019 13:32
Ответ принят как подходящий

Принципиально все операции выполняются последовательно, как указано в ответе Олега.

Что вы должны понимать, так это то, что специальные вещи, такие как операторы for loop или if statements или другие, являются своего рода инструкциями для среды выполнения. И когда выполнение кода доходит до точки, где встречается инструкция for loop, оно продолжается и продолжается внутри цикла for. Он просто знает, что эта вещь внутри цикла for {} должна быть выполнена n раз, прежде чем она сможет продолжиться. Поэтому, когда цикл for завершается, он переходит к следующей строке кода и выполняет любую указанную там инструкцию.

Это не очень глубокое объяснение, но я пытался упростить. Надеюсь, поможет.

спасибо за более понятное объяснение. Я собираюсь проголосовать, потому что ваш ответ внес некоторую ясность в мой вопрос, хотя и не дал мне четкого ответа. Комментарий Найема под вопросом был тем, что я искал, но ваш ответ помог мне сделать его ответ более понятным. Спасибо!

Lance Samaria 19.04.2019 13:40

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

Lance Samaria 19.04.2019 13:49

Программы выполняются последовательно на языке программирования, обучаются начиная с языка Си. Только условные операторы могут изменить поток выполнения, в противном случае он выполняется построчно.

В iOS есть понятие closure, closure — это кусок кода, который занял время для execution. Closure не держит программу и запускается отдельно, пока она не будет закончена. Другие строки кода будут выполняться параллельно. Вот почему ваше утверждение печатается перед loop. если вы хотите печатать после цикла, вы должны сделать это:

    DispatchQueue.main.sync {
       for num in 1...10000000 {

           if num == 10000000 {
            print("why does this print") // the for-loop has to iterate 10 million times for it to print
            }
        }
    }
    print("before this prints")

Есть веская причина, по которой for, while, do-while называются петля, потому что управление выполнением продолжает зацикливаться внутри блока цикла for или while до тех пор, пока не будет выполнено одно из его условий...

Например

for num in 1...4 {

    print("line"\(num))
}
print("line5")

но для компилятора Так что это последовательно, как

line1

line2

line3

line4


line5

Дело не в том, что последняя функция печати работает медленнее, чем цикл for. Если вы запустите цикл for и последнюю функцию print() одновременно, вы сможете это увидеть. В приведенном выше коде это просто вопрос последовательности. Позвольте мне попытаться изложить это перед вами на простом примере ниже.

Предположим, вы находитесь в точке A и хотите пройти в точку B на расстоянии 10 км. Вы начали пешком, чтобы добраться до места назначения (местоположение B). Теперь, находясь в пути, вы можете принимать следующие решения:

  1. Вы можете завершить всю поездку и добраться до места B пешком (это займет больше времени).

  2. В любой момент вашего путешествия вы можете воспользоваться транспортным средством и пройти оставшуюся часть пути (быстрее, чем № 1).

  3. В любой момент во время вашего путешествия в точку B вы можете полностью отменить поездку.

Могут быть и другие сценарии. Однако в любом случае сможете ли вы добраться до точки B до того, как преодолеете расстояние между точкой A и точкой B?

Рассмотрим здесь цикл for как путешествие между точками A и B, а функцию print — банку сладких леденцов, которую вы получите в точке B.

Я надеюсь, что это ясно. Спасибо.

Все циклы (например, for, while, do-while и т. д.) являются синхронными, а поток управления идет сверху вниз. Цикл будет продолжать работать до тех пор, пока не будет достигнуто его условие завершения. Вот почему выполнение первого цикла завершится, а затем будет выполнен ваш внешний оператор печати. Теперь, чтобы контроллер понял, что какое-то задание, которое вы хотите запустить асинхронно, вы можете сделать так:

DispatchQueue.global().async {
    for num in 1...10000000 {

        if num == 10000000 {
            print("why does this print") // the for-loop has to iterate 10 million times for it to print
        }
    }
}

print("before this prints")

Выход:

до этого печатает

почему это печатает

В этой приведенной выше строке кода DispatchQueue.global().async вы сообщаете контроллеру, что блок кода, который вы можете запустить в фоновом режиме, и когда вы закончите, просто скажите мне ответ, поэтому вы можете увидеть, как ниже оператор печати выполняется раньше, чем отображается результат миллиона циклов.

Я попытался дать вам идею настолько просто, насколько это возможно, в моем комментарии к вашему вопросу. Это должно ответить на ваш вопрос. У вас есть и другие ответы, хотя некоторые из них довольно расплывчаты по сравнению с вашим уровнем понимания.

Для полноты и пояснения использования for (или любого другого цикла) я расширяю идею в этом ответе настолько просто, насколько это возможно.

for — это сокращенный синтаксис для многократного выполнения операторов. Код в вашем вопросе может быть записан без этого сокращенного синтаксиса как:

/// checkpoint #1: variable initialization
var num = 1

/// checkpoint #2: condition checking
if num <= 10000000 { //condition true
    if num == 10000000 { //at this point, num equals to 1, so condition is false
        print("why does this print") //doesn't execute
    }
    /// checkpoint #3: increment value of the variable
    num = num + 1 //at this point, num equals to 2
}

if num <= 10000000 { //condition true
    if num == 10000000 { //at this point, num equals to 2, so condition is false
        print("why does this print") //doesn't execute
    }
    num = num + 1 //at this point, num equals to 3
}

if num <= 10000000 { //condition true
    if num == 10000000 { //at this point, num equals to 3, so condition is false
        print("why does this print") //doesn't execute
    }
    num = num + 1 //at this point, num equals to 4
}

. . .
// upto the point where the above repeated lines reach 10000000 if counted
. . .

if num <= 10000000 { //condition true
    if num == 10000000 { //at this point, num equals to 10000000, so condition is true
        print("why does this print") //this time it executes
    }
    num = num + 1 //at this point, num equals to 10000000 + 1
}

if num <= 10000000 { //condition false
    //doesn't execute anything inside this conditional block
    if num == 10000000 {
        print("why does this print")
    }
    num = num + 1
}

// this is the termination point if loop was used

//after executing the previous statements, this line will be executed
print("before this prints") 

Как программисты, мы умеем идентифицировать повторяющиеся операторы. И мы достаточно умны, чтобы сделать приведенный выше код короче и проще. Это когда мы представляем loop. Найдите повторяющийся блок и поместите его в цикл.


Вы заметили повторяющиеся утверждения выше? Давайте поместим это здесь еще раз:

if num <= 10000000 {
    if num == 10000000 {
        print("why does this print")
    }
    num = num + 1
}

Посмотрите этот код в Быстрый:

for num in 1...10000000 {
    if num == 10000000 {
        print("why does this print")
    }
}
print("before this prints")

можно написать на другом языке (скажем, С):

for(int num = 1; num <= 10000000; num++) {
    if (num == 10000000) {
        printf("why does this print");
    }
}
printf("before this prints");

Приведенный выше цикл в С можно разбить на части, которые вы можете сравнить с контрольными точками, упомянутыми в первом блоке кода:

for(int num = 1/*checkpoint #1*/; num <= 10000000/*checkpoint #2*/; num++/*checkpoint #3*/)

Теперь, когда контрольные точки выполняются самим синтаксисом цикла for, остается только одна часть:

if num == 10000000 {
    print("why does this print")
}

И вы помещаете эту часть в фигурные скобки { ... } цикла for.



Я надеюсь, что это подробное объяснение даст вам общее представление о цикле. И теперь вы должны понять поток управления выполнения кода.

Я действительно понял это, основываясь на вашем ответе и ответе @inokey. Нетрудно понять, я просто не знал, что это так работает. Спасибо

Lance Samaria 19.04.2019 16:28

@LanceSamaria о, хорошо, рад это знать. Тогда просто постарайся написать длинное объяснение ни за что. Ваше здоровье!

nayem 19.04.2019 16:31

Это никогда не зря. Следующий человек, который придет, может использовать его ?

Lance Samaria 19.04.2019 16:32

Другие вопросы по теме