这段代码逻辑有点绕, 你们会这么写么?

2022-09-05 09:45:55 +08:00
 FaiChou

看斯坦福的 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 项目中真是普遍使用这种逻辑?

2919 次点击
所在节点    程序员
5 条回复
Building
2022-09-05 10:01:13 +08:00
换我我也用斯坦福的方法,可以确保状态是同步的,非必要不用额外变量记录状态
kujio
2022-09-05 10:03:39 +08:00
不应该是咋想的就咋写吗,写完觉得不够优雅再结合进度慢慢优化。
Building
2022-09-05 10:10:55 +08:00
你的方法在顺序点击翻牌的时候是可以的,但是多线程模拟翻牌的时候可能会出现状态不一致的 bug
zongwan
2022-09-05 22:31:59 +08:00
老師的模板全面, 畢竟不止教一個學生
你能簡化重構老師的, 教學目的也達到了

另外你的在兩次點擊同一張卡牌時有邏輯錯誤
zongwan
2022-09-05 22:40:52 +08:00
此外如果擴展游戲功能 - 重開洗牌
你的代碼需要注意重置 lastRememberedCard
---
老師都沒講給你, 説明老師很佛係
你還是自己多實踐來感悟吧

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/877724

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX