Поиск первого элемента списка, удовлетворяющего некоторому критерию

Проблема

Требуется найти первый элемент списка, удовлетворяющего некоторому критерию (или индекс этого элемента). Возможна и другая формулировка — определить, проходит ли проверку хотя бы один элемент. Критерий может быть как простым («Присутствует ли элемент в списке?»), так и сложным («Имеется список работников, отсортированный в порядке убывания оклада. У кого из менеджеров самый высокий оклад?»). В простых случаях дело обычно ограничивается значением элемента, но если сам массив может изменяться, вероятно, следует определять индекс первого подходящего элемента.

Решение

Перебирайте элементы в цикле fоreach и вызовите last, как только критерий будет выполнен:
my ($match, $found, $item);
foreach $item (@array) {
  if ($criterion) {
    $match = $item;      # Необходимо сохранить
    $found = 1;
    last;
  }
}
if ($found) {
  # Сделать что-то с $match
} else {
    # неудачный поиск
  }
Чтобы определить индекс, перебирайте все индексы массива и вызывайте last, как только критерий выполнится:
my ($i, $match_idx);
for ($i = 0; $i < @аrrау; $i++) {
  if ($criterion)  {
    $match_idx = $i;     # Сохранить индекс
    last;
  }
}
if(defined $match_idx) {
  # Найден элемент $array[$match_idx]
} else {
    # Неудачный поиск
  }

Комментарий

Стандартных механизмов для решения этой задачи не существует, поэтому мы напишем собственный код для перебора и проверки каждого элемента. В нем используются циклы fоreach и for, а вызов last прекращает проверку при выполнении условия. Но перед тем, как прерывать поиск с помощью last, следует сохранить найденный индекс.
Одна из распространенных ошибок — использование функции grep. Дело в том, что grep проверяет все элементы и находит все совпадения; если вас интересует только первое совпадение, этот вариант неэффективен.
Если нас интересует значение первого найденного элемента, присвойте его переменной $match. Мы не можем просто проверять $item в конце цикла, потому что foreach автоматически локализует переменную-итератор и потому не позволяет узнать ее последнее значение после завершения цикла.
Рассмотрим пример. Предположим, в массиве @employees находится список объектов с информацией о работниках, отсортированный в порядке убывания оклада. Мы хотим найти инженера с максимальным окладом; это будет первый инженер в массиве. Требуется только вывести имя инженера, поэтому нас интересует не индекс, а значение элемента.
foreach $employee (@employees)  {
  if ( $employee->category() eq 'engineer' ) {
    $highest_engineer = $employee;
    last;
  }
}
print "Highest paid engineer is: ", $highest_engineer->name(), "\n";
Если нас интересует лишь значение индекса, можно сократить программу — достаточно вспомнить, что при неудачном поиске $i будет содержать недопустимый индекс. В основном экономится объем кода, а не время выполнения, поскольку затраты на присваивание невелики по сравнению с затратами на проверку элементов списка. Однако проверка условия if ($i < @ARRAY) выглядит несколько туманно по сравнению с очевидной проверкой defined из приведенного выше решения.
for  ($i = 0; $i < @ARRAY; $i++)  {
  last if $criterion;
}
if ($i < @ARRAY) {
  # Критерий выполняется по индексу $i
} else {
    # Неудачный поиск
  }

См. также




2013-09-10 17:05:19

Proverte kod v komentariyah gde pro list tam oshibki detskie




Оставить комментарий:
Ваше Имя:
Email:
Антибот: *  
Ваш комментарий: