Для петель и переключателей в Swift - PullRequest
0 голосов
/ 15 апреля 2020

Я (относительно) новичок в программировании. Я работал над «Знакомством с разработкой приложений» от Apple, и так как нет какой-то брошюры с ответами, мне пришлось самому решать вопросы. Однако есть некоторые вещи, которые меня немного смущают:

Есть проблема, над которой я работаю:

Эта игровая площадка имеет встроенный тип Chicken. Цыпленок обладает свойством породы и характера, и оба свойства являются перечислениями.

Вот массив цыплят: цыплята = [{silk ie, сварливый} ...]

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

var chickenOfInterestCount = 0

for chicken in chickens {
    switch chicken.temper {
    case .hilarious:
        switch chicken.breed {
        case .leghorn:
            chickenOfInterestCount += 1
        default:
            chickenOfInterestCount += 0
        }
    default:
        chickenOfInterestCount += 0
    }
}


chickenOfInterestCount

Это работает, но мне интересно, есть ли более эффективный способ сделать это? Могу ли я заставить переключатель проверять каждую курицу на наличие {leghorn, hilarious} и подсчитывать количество необходимых цыплят напрямую, а не использовать вложенный переключатель? (Все мои попытки попробовать это сами были подавлены страшно выглядящими сообщениями об ошибках, поэтому я догадываюсь, что нет). Исходя из этого, как работают циклы?

Когда я изначально изучал циклы, у меня сложилось впечатление, что целые числа использовались для l oop, например, первый l oop был i = 0, и код внутри выполнялся, затем второй l oop было i = 1, et c, где «i» действительно могло быть чем угодно, и идея была бы такой же. Здесь используется "курица". Почему я могу установить условие для chicken.temper? Что значит курица здесь? A для l oop учитывает тип массива, по которому он проходит? Так что же на самом деле происходит здесь, что-то вроде: «возьми 0-го цыпленка в массиве цыплят, назови его« цыпленком », а затем проверь его нрав» »*

Ответы [ 3 ]

1 голос
/ 15 апреля 2020

Swift-y, что нужно сделать в этих ситуациях, это выделить map () , lower () , compactMap () и filter () - в вашем случае filter () :

let chickens: [Chicken] = [ silkie, grumpy, dopey, foghorn ]

let hilariousLeghorns = chickens.filter { 
    $0.breed == .leghorn 
        && $0.temper == .hilarious 
}

let chickenOfInterestCount = hilariousLeghorns.count

(Спасибо, что сделали ваш пример смешным!)

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

Чтобы получить старую школу за l oop, вы можете через l oop через диапазон:

for i in 1...5 { 
    print(i)
}

Это делает то же самое: i присваивают значения 1, 2, 3, 4 и 5, по очереди, повторяя цикл 5 раз.

1 голос
/ 15 апреля 2020

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

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

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

var chickenOfInterestCount = 0

for chicken in chickens {
    if chicken.breed == .leghorn && chicken.temper == .hilarious {
        chickenOfInterestCount += 1
    }
}

chickenOfInterestCount

Switch более полезен, когда вам нужно что-то сделать для каждого случая - в итоге он оказывается намного чище, чем несколько, если / else if высказывания.

var bantamCount = 0
var leghornCount = 0
var unknownBreedCount = 0

var chillCount = 0
var hilariousCount = 0
var unknownTemperCount = 0

for chicken in chickens {

    switch chicken.breed {
    case .bantam:
        bantamCount += 1
    case .leghorn:
        leghornCount += 1
    default:
        unknownBreedCount += 1
    }

    switch chicken.temper {
    case .chill:
        chillCount += 1
    case .hilarious:
        hilariousCount += 1
    default:
        unknownTemperCount += 1
    }

}

Также по вашему вопросу:

Так что же здесь на самом деле происходит, что-то вроде «возьми 0-го Цыпленка в массиве цыплят, назови это «курица», тогда проверь его нрав »?

Как указал Бен, ты правильно об этом рассуждаешь. В области действия l oop есть переменная по имени chicken, которая является экземпляром Chicken (предположительно - не знаю, как вы назвали этот класс или структуру) и является членом массива цыплят.

1 голос
/ 15 апреля 2020

Добро пожаловать в программирование. Это модель для вашего сценария.

  struct Chicken{
        let id:Int
        let breed:String
        let temper:String
    }

    enum Breed{
        static let leghorn = "leghorn"
        static let other = "other"
    }

    enum Temper{
        static let hilarious = "hilarious"
        static let other = "other"
    }


    var chickens:[Chicken] = []
    chickens.append(Chicken(id: 1, breed:.leghorn, temper:.hilarious))
    chickens.append(Chicken(id: 2, breed:.other, temper:.hilarious))
    chickens.append(Chicken(id: 3, breed:.leghorn, temper:.other))

Вы можете получить свой результат из одной строки с помощью Swift . Используйте функцию фильтра. Это похоже на l oop и $ 0 означают текущий элемент. Это l oop через ваш массив и поиск элементов, которые удовлетворяют вашему условию.

print(chickens.filter({$0.breed == .leghorn && $0.temper == .hilarious}).count)

Документация Apple

Дополнительная информация

...