Почему моя страница попадает в бесконечный цикл?

#javascript #random #html-table #infinite-loop #innerhtml

#javascript #Случайный #html-таблица #бесконечный цикл #innerhtml

Вопрос:

 function randomNumber(){
    var value;
    var flag = false;
    var tds = document.querySelectorAll('td');
    do{
        value = Math.round(Math.random() * (26 - 1)   1);
        for(var t = 0; t < tds.length; t  ){
            if(tds[t].innerHTML == value)
                flag = true;
        }
        if(!flag){
            return value;
        }
    }while(flag == true)
}
 

Эта функция возвращает случайное число для innerHTML нового td. В случае, если есть другие td с тем же номером, который генерирует этот код, цикл начинается снова. Если сгенерированное число уникально, я добавляю его в innerHTML нового td. Но я даже не могу загрузить страницу, так как я попадаю в бесконечный цикл, но как бы я ни старался, я не мог заметить проблему в логике этого кода.

Комментарии:

1. Какое условие следует установить flag как false ?

2. Вопрос: Почему моя страница попадает в бесконечный цикл? Ответ: Вероятно, потому, что «значение», по-видимому, всегда равно «tds [t].innerHTML», а флаг никогда не остается «ложным». ПРЕДЛОЖЕНИЕ: пройдите по коду в отладчике (например, Chrome Developer Tools) и посмотрите на «значение» по мере выполнения цикла.

Ответ №1:

Как только ваш цикл найдет случай, для которого tds[t].innerHTML == value он установлен flag true — на этом этапе вы никогда не сможете завершить цикл, потому что нигде вы не проверяете случай, для которого вы можете установить flag значение false , поэтому ваше условие цикла всегда будет истинным.

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

 const arr = []
function randomNumber(){
    var value;
    var flag = false;
    var tds = arr
    var iterations = 0

    do {
        value = Math.round(Math.random() * (26 - 1)   1);
        for(var t = 0; t < tds.length; t  ){
            if(tds.includes(value))
                flag = true;
        }
        if(!flag){
            return value;
        }
        iterations  = 1
        console.log(iterations)
    } while(flag == true amp;amp; iterations < 5000)
}

for (let i = 0;i<20;i =1) {
  arr.push(randomNumber())
}
console.log(arr) 

Комментарии:

1. Вы получили arr доступ из функции in и out — не очень хорошо иметь глобальные переменные — вы действительно не должны писать такой код. Также — почему вы повторяете 5000 раз, если, например, все 26 идентификаторов уже используются? Вы потеряете более 4800 итераций, когда вы могли бы просто проверить, есть ли у вашей функции хотя бы небольшой шанс вернуть все, что она должна

2. Отмеченные моменты — это пример, призванный проиллюстрировать принципы и показать, как продолжались итерации, а не реализация наилучшего практического решения.

3. Тем не менее, если бы вы получили это в своем коде, и этот код был запущен в производство — это было бы очень плохо

Ответ №2:

В тот момент, когда ваша функция хотя бы один раз установит значение flag true, оно закончится — оно больше никогда не устанавливает его в false. Чтобы исправить это, я добавил одну строку кода.

 function randomNumber(){
    var value;
    var flag = false;
    var tds = document.querySelectorAll('td');
    do {
        flag = false; // this line i added
        value = Math.round(Math.random() * (26 - 1)   1);
        for(var t = 0; t < tds.length; t  ){
            if(tds[t].innerHTML == value)
                flag = true;
        }
        if(!flag){
            return value;
        }
    }while(flag == true)
}
 

Я также напишу для вас немного более эффективный код

 function randomNumber(){
    var value;
    var found = false;
    var tds = document.querySelectorAll('td');
    var existingIds = [];
    tds.forEach(td => existingIds.push(td.innerHHML)); // fill up the ids
    do {
        value = Math.round(Math.random() * (26 - 1)   1); // this line would make problems (comment below)
        if (existingIds.indexOf(value) === -1) found = true; // check if value can be found in existing ids and if found - set dount to true (you can also return from here, but i would rather user break (if there was more code after this line, than use retur in the middle of any loop;
    } while(found === false)

    return value;
}
 

Комментарий к строке со случайным:

  • random() возвращает число от 0 до 1
  • как вы это написали — это значение будет случайным числом от 1 до 26 (только это значение).
  • если все значения уже использованы, то наш цикл не будет заканчиваться (мы никогда не смогли бы найти значение от 1 до 26, которое не используется, когда все значения от 1 до 26 уже используются.

Что можно сделать

  • В этом случае вы можете добавить некоторый счетчик (как это сделал @Ben) и выйти из цикла.
  • Или вы можете увеличить число 26 до гораздо большего
  • Вы можете использовать последовательные числа (получить все, взять максимальное, добавить 1 и вернуть это как новое число)

Вы, конечно, можете найти другие способы противостоять этому

Комментарии:

1. Вау, я просто немного раздражен, что все так просто. Я потратил на это около 30 минут. Большое спасибо.

2. Нет проблем, это происходит, когда вы только начинаете свое приключение с программированием / языком

3. @Дмитрий Уткин — Пожалуйста, ознакомьтесь с хорошим отладчиком и приобретите привычку пошагово выполнять свой код.