🎱 Gen
可组合、可变换、可控制随机性。
目录
动机
Swift 的随机数 API 功能强大且易于使用。它允许我们从许多基本类型(例如布尔值和数值类型)创建随机值,并且允许我们随机打乱数组和从集合中抽取随机元素。
但是,它并没有让我们容易地扩展随机数 API,也没有提供可组合的 API,这样我们就可以从更简单的单元创建更复杂的随机类型。
Gen 是 Swift 随机数 API 的一种轻量级包装器,可以轻松构建任何类型的自定义生成器。
示例
Gen 的同名类型 Gen
负责产生随机值。通常,您会使用 Gen
中的一个静态变量来获取 Gen
值。
Gen.bool
// Gen<Bool>
Gen
不是立即生成随机值,而是描述一个可以通过调用其 run
方法生成的随机值。
let myGen = Gen.bool
// Gen<Bool>
myGen.run() // true
myGen.run() // true
myGen.run() // false
Swift 中的每个随机函数都可以在 Gen
的静态函数上使用。
Swift 的 API | Gen 的 API |
---|---|
Int.random(in: 0...9) |
Gen.int(in: 0...9) |
Double.random(in: 0...9) |
Gen.double(in: 0...9) |
Bool.random() |
Gen.bool |
[1, 2, 3].randomElement() |
生成元素为:[1, 2, 3]的生成器 |
将[1, 2, 3]进行随机排序 |
使用Gen.shuffle([1, 2, 3])进行排序 |
使用Gen
类型包装随机性的强大之处在于,我们可以使Gen
类型可组合。例如,整数生成器可以通过简单的map
函数转换为一个数字字符串生成器
let digit = Gen.int(in: 0...9) // Gen<Int>
let stringDigit = digit.map(String.init) // Gen<String>
stringDigit.run() // "7"
stringDigit.run() // "1"
stringDigit.run() // "3"
这已经是一种Swift API默认不提供的随机性形式。
Gen提供了许多生成新类型随机性的运算符,例如map
、flatMap
和zip
,以及用于生成随机数组、集合、字典、字符串、分布等的辅助函数!例如,随机密码生成器只需几个运算符。
// Take a generator of random letters and numbers.
let password = Gen.letterOrNumber
// Generate 6-character strings of them.
.string(of: .always(6))
// Generate 3 segments of these strings.
.array(of: .always(3))
// And join them.
.map { $0.joined(separator: "-") }
password.run() // "9BiGYA-fmvsOf-VYDtDv"
password.run() // "dS2MGr-FQSuC4-ZLEicl"
password.run() // "YusZGF-HILrCo-rNGfCA"
这种组合方式使得我们可以简单地生成任何内容的随机值。
// Use `zip` to combine generators together and build structures.
let randomPoint = zip(.int(in: -10...10), .int(in: -10...10))
.map(CGPoint.init(x:y:))
// Gen<CGPoint>
但组合性并不是Gen
类型闪光的唯一原因。通过延迟随机值的创建,直到调用run
方法,我们可以控制需要确定性的环境中的随机性,例如测试。`run`方法有一个重载版本,它接受一个RandomNumberGenerator
值,这是Swift的协议,为其随机API提供动力。默认情况下,它使用SystemRandomNumberGenerator
,这是一个良好的随机数来源,但我们也可以提供一个可播种的“伪”随机数生成器,以便在测试中获得可预测的结果。
var lcrng = LCRNG(seed: 0)
Gen.int(in: 0...9).run(using: &lcrng) // "8"
Gen.int(in: 0...9).run(using: &lcrng) // "1"
Gen.int(in: 0...9).run(using: &lcrng) // "7"
lcrng.seed = 0
Gen.int(in: 0...9).run(using: &lcrng) // "8"
Gen.int(in: 0...9).run(using: &lcrng) // "1"
Gen.int(in: 0...9).run(using: &lcrng) // "7"
这意味着您不必在应用中使用随机性时牺牲可测试性。
有关使用Gen构建复杂随机性的更多示例,请参阅我们关于创建Zalgo生成器的博客文章,以及关于创建生成式艺术的两个部分视频系列(第1部分和第2部分)。
安装
Carthage
如果您使用Carthage,请将以下依赖项添加到您的Cartfile
github "pointfreeco/swift-gen" ~> 0.1
CocoaPods
如果您的项目使用CocoaPods,只需将其添加到您的Podfile
pod 'PointFree-Gen', '~> 0.1'
SwiftPM
如果您想在使用 SwiftPM 的项目中使用 Gen,只需在您的 Package.swift
文件中添加一个 dependencies
条款即可。
dependencies: [
.package(url: "https://github.com/pointfreeco/swift-gen.git", from: "0.1.0")
]
Xcode Sub-project
将 Gen 作为子模块克隆或下载,然后将 Gen.xcodeproj
拖入您的项目中。
想要了解更多?
这些概念(以及其他概念)在 Point-Free 中得到了深入探讨,这是一个由 Brandon Williams 和 Stephen Celis 主持的功能式编程和 Swift 视频系列。
本库的设计在以下 Point-Free 集中进行了探讨
- 第 30 集:组合式随机性
- 第 31 集:可解码随机性:第一部分
- 第 32 集:可解码随机性:第二部分
- 第 47 集:可预测随机性:第一部分
- 第 48 集:可预测随机性:第二部分
- 第 49 集:生成式艺术:第一部分
🆓 - 第 50 集:生成式艺术:第二部分
🆓
许可证
所有模块均采用 MIT 许可证发布。有关详细信息,请参阅 LICENSE。