V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
FaiChou
V2EX  ›  程序员

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

  •  
  •   FaiChou ·
    FaiChou · 2022-09-05 09:45:55 +08:00 · 2762 次点击
    这是一个创建于 600 天前的主题,其中的信息可能已经有所发展或是发生改变。

    看斯坦福的 SwiftUI 教程, 里面有个卡片连连看的游戏, 比如有 3 对卡, 点击后翻面, 当翻两个卡牌相同时候, 这两个相同的卡牌会消失; 当翻两个时候没有匹配, 点击下一个卡牌时候, 前两个卡牌需要自动翻到背面.

    card game

    我想到的逻辑代码(伪代码):

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

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

    另外你的在兩次點擊同一張卡牌時有邏輯錯誤
    zongwan
        5
    zongwan  
       2022-09-05 22:40:52 +08:00
    此外如果擴展游戲功能 - 重開洗牌
    你的代碼需要注意重置 lastRememberedCard
    ---
    老師都沒講給你, 説明老師很佛係
    你還是自己多實踐來感悟吧
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2970 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 77ms · UTC 14:01 · PVG 22:01 · LAX 07:01 · JFK 10:01
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.