arrays - Swift - 混洗过滤后的结构数组不会改变原始数组

标签 arrays swift struct 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
}

问题:

当我运行我的 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 张卡片)
  • 卡片按类型(即:属性)分隔
  • 我想过滤属性卡并只洗这些卡
  • 我希望原始数组反射(reflect)这些变化

在我的测试中,数组被打乱;但原始数组保持不变。

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

我可以考虑解决这个问题的另一种明显方法是将我的 struct 更改为 class 或者;也许我需要结构之间的另一层?

所以我的查询是:

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

非常感谢


编辑:

属性数组是唯一要洗牌的项目。

如果我向列表中添加另一个数组,它不应该打乱整个内容。

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
    }

我应该只在不影响支票的情况下洗自己的属性(property)卡。

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

最佳答案

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

您应该像这样将打乱的数组分配回 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 参数通过引用将值传递给函数,或者传递值的内存地址,以便可以直接修改它。检查下面的洗牌功能(它是如何定义和使用的)

(我用一个 swift 扩展替换了 shuffle 函数,以便在 Playground 上使用)

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

阅读 inout 参数 here在 In-Out 参数部分。

从文档中提取:

函数参数默认为常量。尝试从函数体内更改函数参数的值会导致编译时错误。这意味着您不能错误地更改参数的值。如果您希望函数修改参数的值,并且希望这些更改在函数调用结束后持续存在,请将该参数定义为输入输出参数。

您可以通过将 inout 关键字放在参数类型之前来编写输入输出参数。 in-out 参数有一个值,该值被传入函数,被函数修改,并传回函数外以替换原始值。有关 in-out 参数行为和相关编译器优化的详细讨论,请参阅 In-Out 参数。

您只能将变量作为输入输出参数的参数传递。您不能将常量或文字值作为参数传递,因为无法修改常量和文字。当您将变量作为实参传递给输入输出参数时,您可以在变量名称前直接放置一个符号 (&),以指示它可以被函数修改。

编辑:试试这个更新的洗牌函数,传入整个数组,看看是否满足您的需要。

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]
    }
}

关于arrays - Swift - 混洗过滤后的结构数组不会改变原始数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43870177/

相关文章:

ios - Swift:我如何在应用程序关闭时保留数据

arrays - 通过匹配另一个数组中元素的属性从数组中删除元素

c# - 将 [][] 形式的数组转换为 [,] 的最聪明方法?

ios - UIView animateWithDuration 完成 block 被循环中断打断;

swift - 如何从日期对象中提取时区?

php从多维数组中获取最多和最少出现的值

ios - 滚动后 CollectionView 未填充

c - 在 C 中通过指针为结构中的变量赋值

c++ - 在 std::multiset (C++) 中协调 typedef 和结构

c++ - 为什么这个结构的大小是 24?