看斯坦福的 SwiftUI 教程, 里面有个卡片连连看的游戏, 比如有 3 对卡, 点击后翻面, 当翻两个卡牌相同时候, 这两个相同的卡牌会消失; 当翻两个时候没有匹配, 点击下一个卡牌时候, 前两个卡牌需要自动翻到背面.
我想到的逻辑代码(伪代码):
var lastRememberedCard = -1
func choose(cardIndex) {
if lastRememberedCard > -1 {
if cards[cardIndex].content == cards[lastRememberedCard].content {
// matched the card, flag it to `isMatched`
cards[cardIndex].isMatched = true
cards[lastRememberedCard].isMatched = true
}
cards[cardIndex].isFaceUp = true
lastRememberedCard = -1 // reset to -1, next turn
} else {
lastRememberedCard = cardIndex
cards.forEach { $0.isFaceUp = $0 == cardIndex }
}
}
但是斯坦福老师写的代码, 一开始逻辑是上面的, 但经过重构后变成下面这样:
struct MemoryGame<CardContent> where CardContent: Equatable {
private var indexOfTheOneAndOnlyFaceUpCard: Int? {
get { cards.indices.filter({ cards[$0].isFaceUp }).oneAndOnly }
set { cards.indices.forEach { cards[$0].isFaceUp = $0 == newValue } }
}
mutating func choose(_ card: Card) {
if let chosenIndex = cards.firstIndex(where: { $0.id == card.id }),
!cards[chosenIndex].isFaceUp,
!cards[chosenIndex].isMatched
{
if let potentialMatchIndex = indexOfTheOneAndOnlyFaceUpCard {
if cards[chosenIndex].content == cards[potentialMatchIndex].content {
cards[chosenIndex].isMatched = true
cards[potentialMatchIndex].isMatched = true
}
cards[chosenIndex].isFaceUp = true
} else {
indexOfTheOneAndOnlyFaceUpCard = chosenIndex
}
}
}
init() {...}
struct Card: Identifiable {...}
}
extension Array {
var oneAndOnly: Element? {
self.count == 1 ? self.first : nil
}
}
老师的方法使用了 swift 中 Computed Properties, 比较绕的逻辑:
正常逻辑是, 当偶数次点击时候, 将 flag 置空, 就像我的代码中 lastRememberedCard = -1
.
老师的逻辑是, 当偶数次点击时候不去管, 当下一次点击时候, 使用 get 方法来获取是否只有一个卡牌被翻正, 是的话就返回这卡牌的下标, 否则返回 nil.
通过这个逻辑, 这么一想, 我手动在偶数次将 lastRememberedCard = -1
重置的作用就是为了下一次使用时候它是空的, 我的逻辑比较 explicit.
所以目前疑惑老师这段代码逻辑是为了教学(swift computed property), 还是 Swift 项目中真是普遍使用这种逻辑?
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.