首页 文章

Swift - 对已过滤的结构数组进行混洗不会更改原始数组

提问于
浏览
0

我正在写一个卡片游戏,其中一些卡片收集在一个阵列中 .

卡的数据结构相同,但数据不同 .

注意:我的shuffle确实有效 .

我想过滤这个数组,只是洗掉我过滤过的卡片 .

然而,虽然我可以洗牌,但我注意到我的原始卡片阵列根本没有变化 .

我认为这个问题是因为我的卡片模型是结构而不是类 .

更多背景信息 .

虽然每张卡的数据不同,但结构完全相同;两种类型都有名称和数值 .

这是建模的;

enum FSCardType: Int {
    case property
    case anotherType
    case yetAnotherType
}

// Card Model
struct FSCard {
    let type: FSCardType
    let name: String
    let value: Int

    var description: String {
        return ("Name: \(self.name) Value: \(self.value), Type: \(self.type)")
    }
}

我在这样的静态函数中创建我的卡片:

class FSCardAPI: NSObject {

    public static func createCards() -> [FSCard] {
        var cards:[FSCard] = [FSCard]()
        let properties:[FSCard] = FSCardAPI.createProperties()

        cards.append(contentsOf: properties)

        return cards
    }

    public static func shuffleCards(cards:[FSCard]) -> [FSCard] {
        let shuffled:[FSCard] = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: cards) as! [FSCard]
        return shuffled
    }

    fileprivate static func createProperties() -> [FSCard] {
        var properties:[FSCard] = [FSCard]()
        for idx in 1...30 {
            let property:FSCard = FSCard(type: .property, name: "Property #\(idx)", value: idx, owner: nil)
            properties.append(property)
        }
        return properties
    }
}

好的,现在我只想在这个卡阵列中洗牌我的属性卡 .

所以在我的 XCTest 文件中首先过滤所有类型的卡: property

func testShuffleProperties() {
        var propertyCards  = gameModel.cards.filter { (fs:FSCard) -> Bool in
            return (fs.type == .property)
        }

        propertyCards = FSCardAPI.shuffleCards(cards: propertyCards)

        print(propertyCards)
        print(" ---- ")
        print(gameModel.cards)       
}

这叫:

// Inside my FSCardAPI 
public static func shuffleCards(cards:[FSCard]) -> [FSCard] {
    let shuffled:[FSCard] = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: cards) as! [FSCard]
    return shuffled
}

The issue:

当我运行我的XCTest时,我注意到在 propertyCards 被洗牌时,我的 gameModel.cards 没有被洗牌;

Test Case 'testShuffleProperties' started.

// contents of `propertyCards`
[FSCard(type: FSCardType.property, name: "Property #4", value: 4, owner: nil), 
FSCard(type: FSCardType.property, name: "Property #16", value: 16, owner: nil), 
// .. etc

// contents of `gameModel.cards`
[FSCard(type: FSCardType.property, name: "Property #1", value: 1, owner: nil), 
FSCard(type: FSCardType.property, name: "Property #2", value: 2, owner: nil), 
// .. etc

摘要

  • 我有一系列牌(即:30张牌)

  • 卡按类型分隔(即:属性)

  • 我想过滤属性卡并仅将这些卡洗牌

  • 我希望原始数组能够反映这些变化

在我的测试中,数组被洗牌;但原始阵列保持不变 .

我能想到的一种方法是我做所有的洗牌,然后按卡片类型对数组进行排序,然后更新gameModel.cards,但这似乎有点过头了 .

我能想到解决这个问题的另一个显而易见的方法是将我的 struct 改为 class 或;也许我需要在结构之间的另一层?

所以我的查询是:

如何过滤结构数组,只调整这些结果并更改原始数组的状态?

非常感谢


编辑:

properties数组是唯一要随机播放的项目 .

如果我在列表中添加另一个数组,则不应该对整个内容进行随机播放 .

IE浏览器;如果我这样做:

public static func createCards() -> [FSCard] {
        var cards:[FSCard] = [FSCard]()
        let properties:[FSCard] = FSCardAPI.createProperties()
        let cheqs:[FSCard] = FSCardAPI.createCheques()

        cards.append(contentsOf: properties)
        cards.append(contentsOf: cheqs)

        return cards
    }

我应该只在自己内部洗牌,而不会影响cheq .

我想我可以让它更简单,只有2个数组,但同时我认为没有理由这样做,因为数据结构是相同的 .

2 回答

  • 1

    您没有分配给gameModel.cards而不是实际更改数组 . 所以我不希望gameModel.cards被洗牌 .

    您应该将shuffled数组分配回gameModel.cards,如下所示:

    func testShuffleProperties() {
        var propertyCards  = gameModel.cards.filter { (fs:FSCard) -> Bool in
            return (fs.type == .property)
        }
    
        propertyCards = FSCardAPI.shuffleCards(cards: propertyCards)
        gameModel.cards = propertyCards
    
        print(propertyCards)
        print(" ---- ")
        print(gameModel.cards)       
    }
    

    或者如果你想直接改变数组,你应该看看通过引用传递卡片,或者在Swift中使用inout参数 . inout参数通过引用将值传递给函数,或者传递值的内存地址,以便可以直接修改它 . 检查下面的随机播放卡功能(如何定义和使用)

    (为了便于在操场上使用,我用快速扩展替换了shuffle功能)

    extension MutableCollection where Indices.Iterator.Element == Index {
        /// Shuffles the contents of this collection.
        mutating func shuffle() {
            let c = count
            guard c > 1 else { return }
    
            for (firstUnshuffled , unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) {
                let d: IndexDistance = numericCast(arc4random_uniform(numericCast(unshuffledCount)))
                guard d != 0 else { continue }
                let i = index(firstUnshuffled, offsetBy: d)
                swap(&self[firstUnshuffled], &self[i])
            }
        }
    }
    
    extension Sequence {
        /// Returns an array with the contents of this sequence, shuffled.
        func shuffled() -> [Iterator.Element] {
            var result = Array(self)
            result.shuffle()
            return result
        }
    }
    
    enum FSCardType: Int {
        case property
        case anotherType
        case yetAnotherType
    }
    
    // Card Model
    struct FSCard {
        let type: FSCardType
        let name: String
        let value: Int
    
        var description: String {
            return ("Name: \(self.name) Value: \(self.value), Type: \(self.type)")
        }
    }
    
    class FSCardAPI: NSObject {
    
        public static func createCards() -> [FSCard] {
            var cards:[FSCard] = [FSCard]()
            let properties:[FSCard] = FSCardAPI.createProperties()
    
            cards.append(contentsOf: properties)
    
            return cards
        }
    
        public static func shuffleCards(cards:inout [FSCard]) {
            cards = cards.shuffled()
        }
    
        fileprivate static func createProperties() -> [FSCard] {
            var properties:[FSCard] = [FSCard]()
            for idx in 1...30 {
                let property:FSCard = FSCard(type: .property, name: "Property #\(idx)", value: idx)
                properties.append(property)
            }
            return properties
        }
    }
    
    var cards = FSCardAPI.createCards()
    
    FSCardAPI.shuffleCards(cards: &cards)
    

    输出:

    Output

    在In-Out参数部分中读取inout参数here .

    从文档中发挥:

    默认情况下,函数参数是常量 . 尝试从该函数体内更改函数参数的值会导致编译时错误 . 这意味着您无法错误地更改参数的值 . 如果希望函数修改参数的值,并且希望在函数调用结束后这些更改仍然存在,请将该参数定义为输入输出参数 .

    您可以通过将inout关键字放在参数类型之前来编写输入输出参数 . 输入输出参数具有传递给函数的值,由函数修改,并传递回函数以替换原始值 . 有关输入输出参数和相关编译器优化的行为的详细讨论,请参阅输入输出参数 .

    您只能将变量作为输入输出参数的参数传递 . 您不能传递常量或文字值作为参数,因为不能修改常量和文字 . 当您将变量名称作为参数传递给输入输出参数时,可以在变量名称前面直接放置&符号,以指示它可以由函数修改 .

    EDIT: 尝试这个更新的shuffle函数,传入整个数组,看看你是否做了所需 .

    public static func shuffleCards(cards:inout [FSCard]) {
        let propertyCards = cards.filter({ $0.type == .property })
        let indexes = propertyCards.map { Int(cards.index(of: $0)!) }
        let shuffledCards = propertyCards.shuffled()
    
        for (idx, val) in indexes.enumerated() {
            cards[val] = shuffledCards[idx]
        }
    }
    
  • 1

    问题是,在你洗牌后,你没有这样做什么与他们 . 您应该将原始卡片列表中的属性卡替换为随机属性卡列表中的属性卡 .

    var propertyCardsShuffledOriginalArray = originalArray.map {
    
        var card = $0
    
        if $0.type == .property {
            card = shuffledPropertyCards.first as! FSCard
            shuffledPropertyCards.removeFirst()
        }
    
        return card
    }
    

    propertyCardsShuffledOriginalArray 是你需要的

相关问题