#ios #swift #struct
#iOS #swift #структура
Вопрос:
Я пытаюсь уменьшить количество строк в функции, которую я написал. Для достижения этого мне нужно выбрать свойство структуры на основе некоторого условия и передать это свойство в качестве параметра, чтобы сравнить это свойство с другими элементами в массиве, которые имеют тот же тип структуры. Вот мой гигантский код с повторением одного и того же для разных свойств.
Я попробовал следующий код. Хотя это работает как шарм, но я чувствую, что одна и та же работа выполнялась несколько раз.
func checkContentsForMatching(forIndex: Int) {
var cards = [SetCard]()
for index in game.indicesOfChosenCards.indices {
cards.append(cardTitles[testCards[game.indicesofChosenCards[index]]]!)
}
if (cards[0].color == cards[1].color) amp;amp; (cards[1].color == cards[2].color) amp;amp; (cards[0].color == cards[2].color) {
game.playingCards[game.indicesOfChosenCards[0]].color = true
game.playingCards[game.indicesOfChosenCards[1]].color = true
game.playingCards[game.indicesOfChosenCards[2]].color = true
}
else if cards[0].color == cards[1].color {
game.playingCards[game.indicesOfChosenCards[0]].color = true
game.playingCards[game.indicesOfChosenCards[1]].color = true
}
else if cards[1].color == cards[2].color {
game.playingCards[game.indicesOfChosenCards[1]].color = true
game.playingCards[game.indicesOfChosenCards[2]].color = true
}
else if cards[0].color == cards[2].color {
game.playingCards[game.indicesOfChosenCards[0]].color = true
game.playingCards[game.indicesOfChosenCards[2]].color = true
}
if (cards[0].shape == cards[1].shape) amp;amp; (cards[1].shape == cards[2].shape) amp;amp; (cards[0].shape == cards[2].shape) {
game.playingCards[game.indicesOfChosenCards[0]].shape = true
game.playingCards[game.indicesOfChosenCards[1]].shape = true
game.playingCards[game.indicesOfChosenCards[2]].shape = true
}
else if cards[0].shape == cards[1].shape {
game.playingCards[game.indicesOfChosenCards[0]].shape = true
game.playingCards[game.indicesOfChosenCards[1]].shape = true
}
else if cards[1].shape == cards[2].shape {
game.playingCards[game.indicesOfChosenCards[1]].shape = true
game.playingCards[game.indicesOfChosenCards[2]].shape = true
}
else if cards[0].shape == cards[2].shape {
game.playingCards[game.indicesOfChosenCards[0]].shape = true
game.playingCards[game.indicesOfChosenCards[2]].shape = true
}
Комментарии:
1. Является ли фактическая проблема реализацией наборов? Если да, возможно, вам следует спросить об этом, и я покажу вам, как это реализовать. В нынешнем виде это проблема x-y.
2. Да, это реализация наборов, которая была заданием программирования из CS193P. Я уже это реализовал. Я хотел сжать свой код в 300 строк, если не меньше.
3. Да, моя реализация довольно компактна (конечно, намного компактнее вашей). Существует довольно элегантный способ сделать это.
Ответ №1:
То, что вы ищете, — это быстрый ключевой путь.
В моей реализации Sets (Zotz!) Это то, что я делаю. Сначала я использую перечисления с исходными значениями Int для четырех атрибутов card. Это позволяет мне представлять любой атрибут как общий тип (Int). Я выражаю эти необработанные значения как вычисляемые свойства и предоставляю список всех четырех вычисляемых свойств в виде массива ключевых путей (обратите внимание, это Swift 5):
public struct Card: Codable, CustomStringConvertible {
let itsColor : Color
let itsNumber : Number
let itsShape : Shape
let itsFill : Fill
var itsColorRaw : Int { return itsColor.rawValue }
var itsNumberRaw : Int { return itsNumber.rawValue }
var itsShapeRaw : Int { return itsShape.rawValue }
var itsFillRaw : Int { return itsFill.rawValue }
public static let attributes : [KeyPath<Card, Int>] = [
itsColorRaw, itsNumberRaw, itsShapeRaw, itsFillRaw
]
// ...
}
Хорошо, теперь легко выразить правило, определяющее, является ли данная тройка карт допустимым совпадением:
private func isCorrectCards(_ cards:[Card]) -> Bool {
func evaluateOneAttribute(_ nn:[Int]) -> Bool {
let allSame = (nn[0] == nn[1]) amp;amp; (nn[1] == nn[2])
let allDiff = (nn[0] != nn[1]) amp;amp; (nn[1] != nn[2]) amp;amp; (nn[2] != nn[0])
return allSame || allDiff
}
return Card.attributes.allSatisfy { att in
evaluateOneAttribute(cards.map{$0[keyPath:att]})
}
}
Еще более элегантная версия, предложенная @vacawama:
private func isCorrect(cards:[Card]) -> Bool {
return Card.attributes.allSatisfy { att in
Set(cards.map{$0[keyPath:att]}).count != 2
}
}
Комментарии:
1. Ну, вы вдохновили меня обновить мой код, чтобы использовать keyPath и
allSatisfy
. Мой пересмотренный код стал еще короче и понятнее, чем был раньше!2. Я занимаюсь кодированием уже три месяца, и это моя первая сборка приложения за три или четыре дня. Спасибо за предложение
keypath
иallsatisfy
. Это, несомненно, сделает мой код более компактным.3. Проверка набора с использованием набора Swift:
Set(nn).count != 2
4. @vacawama Вау, круто! Конечно, потому что это 1, если все они одинаковые, и 3, если все они разные. Я могу выбросить
evaluateOneAttribute
и сделать все это как однострочный внутриallSatisfy
блока.5. Что ж, теперь мы показываем оба!
Ответ №2:
Не уверен, что вы пытаетесь сделать, но если вы пытаетесь обобщить if
-s и else
-s, я бы сделал что-то вроде этого:
var allColorsMatch = cards[0].color == cards[1].color) amp;amp; (cards[1].color == cards[2].color) amp;amp; (cards[0].color == cards[2].color
var zeroAndOneMatch = cards[0].color == cards[1].color
var oneAndTwoMatch = cards[1].color == cards[2].color
var zeroAndTwoMatch = cards[0].color == cards[2].color
game.playingCards[game.indicesOfChosenCards[0]].color = allColorsMatch || zeroAndOneMatch || zeroAndTwoMatch
game.playingCards[game.indicesOfChosenCards[1]].color = allColorsMatch || zeroAndOneMatch || oneAndTwoMatch
game.playingCards[game.indicesOfChosenCards[2]].color = allColorsMatch || zeroAndTwoMatch || oneAndTwoMatch
Вот и все, теперь вам не нужны if
-s или else
-s для color
, вы можете заменить color
if-else
блоки приведенным выше кодом.
Теперь вы можете спросить, как бы вы сделали то же самое для shape
. shape
подобный цвет также может быть true
или false
, насколько я вижу. Итак, вы можете захотеть обернуть приведенный выше код в функцию с тремя параметрами и выполнить те же операции.
Ответ №3:
Вы можете использовать функции более высокого порядка Swift для фильтрации вашего массива cards вместо использования цикла for in для запуска.
Вероятно, вам следует создать отдельные объекты модели, чтобы избежать необходимости выполнять вызовы вложенных индексов, такие как cardTitles[testCards[game.indicesOfChosenCards[index]]]!
.
Кроме того, почему ваше свойство color в game.playingCards[game.indicesOfChosenCards[i]].color
логическом значении? Кажется немного запутанным, что свойство с именем color возвращает значение true или false.
Попробуйте разбить свою функцию на несколько функций. Вы пытаетесь сделать слишком много в этом.