可采样 (Sampleable)

一个轻量级协议,用于描述可以提供自身样本的类型。

请注意,此协议的意图不是规定样本是常量还是随机的;它们可以是常量也可以是随机的,具体取决于用例。 将此协议的使用与诸如 SwiftCheckArbitrary 之类的东西结合使用非常容易,可以传递任意值作为样本,或者维护一小组常量样本,并访问任意随机生成的值。

用法

Sampleable 要求其遵循者提供一个静态方法。

static var sample: Self { get }

此方法仅返回遵循类型的样本值。

一个 Sampleable 类型随后可以继续提供任何可选方法,这些方法否则具有合理的默认实现。

static var successSample: Self? { get }

static var failureSample: Self? { get }

static var samples: [Self] { get }

前两个默认为 nil,第三个默认为仅包含 Self.sample 返回值的数组。

除了提供一种提供样本的标准方法之外,Sampleable 协议还提供了一系列静态函数,这些函数可以通过扩展来组合来自其他 Sampleable 类型的样本。 这些函数真正的美妙之处在于,它们采用一个闭包,该闭包使用多个其他类型的样本构造 Self,并且它会传递每个输入类型的一个样本,直到输入类型的样本用完为止。 反过来,它们会生成一个 Self 的数组。 这很难解释,但希望以下示例能阐明其操作。 如果可以这样理解,那么执行的操作本质上是 zipWith,其中每个输入类型的样本都与 Self 的构造函数压缩在一起。

考虑以下三个 Sampleable 遵循者。

extension Int: Sampleable {
  public static var sample: Int { return samples[0] }

  public static var samples: [Int] {
    return [1, 2, 3, 4]
  }
}

extension String: Sampleable {
  public static var sample: String { return samples[0] }

  public static var samples: [String] {
    return ["A", "B"]
  }
}

请注意,String 提供了两个样本,而 Int 提供了四个样本。 这很重要,因为以下操作(很像 zipWith)将在到达最短的样本数组的末尾时停止。

let composedSamples = String.samples(using: String.self, Int.self) { string, int in
  return "\(string)\(int)"
}

这将生成 Strings 数组 ["A1", "B2"]。 请注意,它从每个输入类型中获取了第一个样本并将其传递给闭包,然后对每个输入类型的第二个样本执行了相同的操作,然后它耗尽了第一个输入类型的样本,因此完成了。

这种类型的组合可以开箱即用地与最多 8 种输入类型完成(当然,您可以自己添加更多),并且示例中第一个输入类型与结果类型匹配没有任何特殊之处,这完全是偶然的。