Swift 如何从 CharacterSet 中获取随机元素

How to get random element from CharacterSet in Swift

我有一组用户已经输入的字符。我想从所有字母数字字符、标点字符和符号的组合列表中获取用户尚未输入的随机字符。我查看了文档,但找不到从 CharacterSet 中获取随机元素的方法,这看起来很奇怪......我觉得我遗漏了一些明显的东西。

func newRandomCharacter() -> Character {

    let validCharacters: CharacterSet = .alphanumerics.union(.punctuationCharacters).union(.symbols)

    var setOfUsedCharacters = CharacterSet()
    // usedCharacters: [Character]
    usedCharacters.forEach { setOfUsedCharacters.insert(charactersIn: String([=11=])) }

    let setOfUnusedCharacters = validCharacters.subtracting(setOfUsedCharacters)

    return setOfUnusedCharacters.randomElement() <- ???
}

字符集不是集合。它是一个 SetAlgebra。

尽管它的名字是“CharactersSet”。它是数学意义上的一组 UnicodeScalar(不是字符):定义是否包含给定 UnicodeScalar 的规则列表。没有直接的方法来枚举这些值。由于 UnicodeScalar 在有限范围内,因此生成完整列表的方法效率低下,但它非常庞大。

不过,我很好奇您将如何使用它。这可能包括许多您意想不到的字符,例如 UNDERTIE (‿)、SAMARITAN PUNCTUATION BAU (࠳) 和 THAI CHARACTER FONGMAN (๏)。你真的想从所有 Unicode 字母数字和标点符号中挑选一个随机值吗? (例如,有超过 800 个标点字符,根据我的粗略计算,可能有 25k 个字母数字。我还没有计算符号,但有很多。你在美国键盘上得到一个字符的机会是非常接近于零。)

我想这就是您真正要找的代码:

let asciiRange = 33...126
let randomCharacter = asciiRange.randomElement()
    .flatMap(UnicodeScalar.init)
    .flatMap(Character.init)!

这将 return 一个随机的、可打印的 ASCII 字符。

鉴于您的集合占 Unicode 的很大一部分 space,以下是您如何相当快地获得真正随机的集合:

func randomCharacter() -> Character {
    // Drops the control characters and SPACE, the private use areas, tags, and the variation selectors.
    // The full range is 0x00...0x10FFFD
    let unicodeRange = 0x21...0x2FA1D
    let validCharacters: CharacterSet = .alphanumerics.union(.punctuationCharacters).union(.symbols)

    repeat {
        if let c = unicodeRange.randomElement().flatMap(UnicodeScalar.init),
           validCharacters.contains(c) {
            return Character(c)
        }
    } while true
}

我一直在猜,直到找到一个。只要您从中挑选的集合与完整集合的大小相似,这就会趋于收敛。这可能比通过走类似的 space.

生成巨大的 Set<Character> 更有效