我正在编写一个纸牌游戏,将许多纸牌收集在一个数组中。
卡片的数据结构相同,只是数据不同。
注意:我的随机播放确实有效。
我想过滤这个数组,并且只洗我过滤过的牌。
但是,虽然我可以洗牌,但我注意到我原来的牌阵根本没有改变。
我认为这个问题是因为我的卡片模型是一个结构而不是一个类。
更多背景信息。
虽然每张卡的数据不同,但结构完全一样;两种类型都有一个名称和一个数值。
这样建模的;
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)
输出:
阅读 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/