Давайте решим реальные проблемы с помощью декларативного фреймворка.

Во время выступления WWDC о новой структуре Combine докладчик сказал аудитории, что это в значительной степени комплексное решение, в котором Swift поддерживает декларативное и императивное кодирование вместе друг с другом.

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

Первый проход

Вас попросили разделить список пациентов на две группы, чтобы проверить эффективность вакцины. Это похоже на простую проблему, но читайте дальше. Задача - это нечто большее, чем кажется на первый взгляд. Давайте посмотрим на проблему с точки зрения необработанных данных.

Теперь мой первый монтаж проиллюстрирован анимированным GIF-изображением выше. Я беру свой список пациентов (синие точки) и разбиваю группу на две выборки одинакового размера. Это работает, просто подбрасывая монету по мере того, как она проходит по списку. Головы идут в верхнюю группу (две верхние строчки). Хвосты переходят в нижнюю группу (последние две строчки).

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

Затем я объявляю класс, в котором публикую массив указанных записей и некоторые методы их сортировки. Это императивный код:

Первый метод, sexGroup, просто определяет пол для записей. Второй, splitGroup, делает именно это, случайным образом разбивая группу на две. Но как это сделать в Combine? Вот еще два основных метода:

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

Мне нужно попытаться сбалансировать оба пола - особенно если у меня для начала неравное количество.

Второй проход

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

Здесь нет ракетостроения. По сути, это тот же код. Я играю со сплитом, пока не получу четное количество пациентов мужского и женского пола. Ручное решение, но его можно автоматизировать. Я использую тот же метод для распределения полов в данных, а затем вызываю этот метод, чтобы снова разделить их. Первый показанный здесь код - это обязательное решение:

Второе декларативное решение:

Это работает достаточно хорошо, но есть одна сложность. Как вы наверняка знаете, чем вы старше, тем более уязвимы перед проблемами со здоровьем. Также необходимо учитывать возраст пациентов. Это требование может противоречить выбору мужчины / женщины. Нам нужно еще раз переосмыслить эту процедуру.

Третий проход

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

Чтобы решить эту проблему, я создал еще два метода. Первый - просто отсортировать записи:

func sorted() {
  patients.sort {(lhs: Patient, rhs: Patient) -> Bool in
    return lhs.age < rhs.age
  }
}

А второй - перемежать их, а затем разделять на две группы одновременно:

С декларативной версией здесь:

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

Не стесняйтесь делиться своими взглядами в комментариях.

Заключение

Так что я бы сказал, что сделал это, но я этого не сделал. Видите ли, есть еще кое-что, что нужно учитывать. Такие вещи, как географическая предвзятость, ранее существовавшие условия и раса. Этот список можно продолжить - все пункты потенциально могут вносить предвзятость. Это далеко не так просто, как вы думаете.

В любом случае, мне нужно подвести итоги. Надеюсь, вам понравилось это читать так же, как и мне.

Перед тем, как я уйду, вот код SwiftUI, использующий это. Вам понадобится следующее, если вы хотите попробовать все на себе:

Сохраняйте спокойствие, продолжайте кодировать.