从计时器上的数组中拉出随机项目

Pulling a random item from an array on a timer

我有一个字符串数组。我想从这个数组中随机显示 3 个独特的项目。然后每 5 秒,其中一个项目被另一个独特的项目替换(我的想法是添加一个延迟的动画)。

我可以显示 3 个字符串,但有时它们会重复,并且计时器不会更新 factLabel 标签。

这是我的进度:

override func viewDidLoad() {
    super.viewDidLoad()
    updateUI()
}

func randomFact() -> String {
    let arrayCount = model.cancunFacts.count
    let randomIndex = Int(arc4random_uniform(UInt32(arrayCount)))
    return model.cancunFacts[randomIndex]
}

// Display the facts
func updateUI() {
    Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(randomFact), userInfo: nil, repeats: true)
    factLabel.text = randomFact() + " " + randomFact() + " " + randomFact()
}

如何让文本始终随机更新,而不重复这 3 个事实?

创建索引数组。从数组中删除一个随机索引,用它来索引你的字符串。当索引数组为空时,重新填充。

下面是一些生成随机、非重复字符串的示例代码:

var randomStrings = ["Traitor", "Lord Dampnut", "Cheeto-In-Chief", 
  "F***face Von Clownstick", "Short-Fingered Vulgarian", 
  "Drumpf", "Der Gropenführer", "Pumpkin in a suit"]

var indexes =  [Int]()

func randomString() -> String {
    if indexes.isEmpty {
        indexes = Array(0...randomStrings.count-1)
    }
    let index = Int(arc4random_uniform(UInt32(indexes.count)))
    let randomIndex = indexes.remove(at: index)
    return randomStrings[randomIndex]
}

for i in 1...100 {
    print (randomString())
}

(请注意,在索引数组为空且需要重新填充的情况下,它可能仍会生成重复字符串。您需要添加额外的逻辑来防止这种情况。)

版本 2:

下面是相同的代码,稍作修改以避免在索引数组为空且需要重新填充时重复:

var randomStrings = ["tiny-fingered", "cheeto-faced", "ferret-wearing", "sh*tgibbon"]

var indexes =  [Int]()
var lastIndex: Int?

func randomString() -> String {
    if indexes.isEmpty {
        indexes = Array(0...randomStrings.count-1)
    }
    var randomIndex: Int
    repeat {
        let index = Int(arc4random_uniform(UInt32(indexes.count)))
        randomIndex = indexes.remove(at: index)
    } while randomIndex == lastIndex
    lastIndex = randomIndex
    return randomStrings[randomIndex]
}


for i in 1...10000 {
    print (randomString())
}

即使它使用 repeat...while 语句,重复条件也永远不会连续触发两次,因为除非在重新填充索引数组之后,否则您永远不会得到重复。

使用该代码,如果有重复,则在遍历数组时将跳过所选字符串。为避免这种情况,您需要稍微调整代码以不从数组中删除给定索引,直到您确认它不是重复项。

版本 3:

上面的版本 2,如果它在重新填充数组时选择重复项,将跳过一个条目。我编写了代码的第三个版本,用于重新填充数组,删除它 returned 的最后一个项目,这样它就不能重复,然后在它选择一个随机项目后将其添加回数组。第三个版本在重新填充之前总是 return 源数组中的每个项目,并且永远不会重复一个项目。因此它是真正随机的,没有偏见:

import UIKit

var randomStrings = ["Traitor", "Lord Dampnut", "Cheeto-In-Chief",
                     "F***face Von Clownstick", "Short-Fingered Vulgarian",
                     "Drumpf", "Der Gropenführer", "Pumpkin in a suit"]

var indexes =  [Int]()
var lastIndex: Int?
var indexToPutBack: Int?

func randomString() -> String {
  
  //If our array of indexes is empty, fill it.
  if indexes.isEmpty {
    indexes = Array(0...randomStrings.count-1)
    print("") //Print a blank line each time we refill the array so you can see
    
    //If we have returned an item previously, find and remove that index
    //From the refilled array
    if let lastIndex = lastIndex,
      let indexToRemove = indexes.index(of: lastIndex) {
      indexes.remove(at: indexToRemove)
      indexToPutBack = indexToRemove //Remember the index we removed so we can put it back.
    }
  }
  var randomIndex: Int
  let index = Int(arc4random_uniform(UInt32(indexes.count)))
  randomIndex = indexes.remove(at: index)
  
  //If we refilled the array and removed an index to avoid repeats, put the removed index back in the array
  if indexToPutBack  != nil{
    indexes.append(indexToPutBack!)
    indexToPutBack = nil
  }
  lastIndex = randomIndex
  return randomStrings[randomIndex]
}


for i in 1...30 {
  print (randomString())
}

示例输出:

Short-Fingered Vulgarian
F***face Von Clownstick
Pumpkin in a suit
Drumpf
Lord Dampnut
Traitor
Der Gropenführer
Cheeto-In-Chief

Der Gropenführer
Drumpf
Lord Dampnut
Short-Fingered Vulgarian
Cheeto-In-Chief
Pumpkin in a suit
Traitor
F***face Von Clownstick

Short-Fingered Vulgarian
F***face Von Clownstick
Drumpf
Traitor
Cheeto-In-Chief
Lord Dampnut
Pumpkin in a suit
Der Gropenführer

Lord Dampnut
Short-Fingered Vulgarian
Pumpkin in a suit
Cheeto-In-Chief
Der Gropenführer
F***face Von Clownstick

您的计时器正在调用随机事实,这只是 returns 一个事实,什么都不做。您可能应该有一些名为 initializeTimer 的第三种方法来执行 Timer.scheduledtimer,您应该将其从 updateUI 方法中取出。该计时器应调用 updateUI。这将修复您的标签更新。您也可以在 viewDidLoad 中调用 initializeTimer 而不是 updateUI。至于防止重复事实,Duncan C 的想法是有一个单独的数组,当你设置新的随机事实时从中删除项目然后在它为空时填充备份似乎是个好主意。

维护两个随机字符串数组 usedStringsunusedStrings 可能最简单,如下所示:

var unusedStrings: [String] = ["king", "philip", "calls", "out", "for", "good", "soup"]
var usedStrings: [String] = []

func randomString() -> String {
    if unusedStrings.isEmpty() {
        unusedStrings = usedStrings
        usedStrings = []
    }

    let randomIndex = Int(arc4random_uniform(unusedStrings.count))
    let randomString = unusedStrings[randomIndex]
    usedStrings.append(randomString)

    return randomString
}