Swift - 结合多种折扣类型
Swift - Combine multiple discount types
所以我一直在研究 protocol witness types,它们本质上是协议的具体实现。假设我们有一个对商品打折并返还双倍商品的协议。
而不是这个:
protocol Discountable {
func discount() -> Double
}
一个人这样做:
struct Discounting<A> {
let discount: (A) -> Double
}
并且,不是像这样将一种类型一次符合 Discountable
协议:
extension Double: Discountable {
func discount() -> Double {
return self * 0.9
}
}
可以为一个类型提供多个具体实现:
extension Discounting where A == Double {
static let tenPercentOff = Self { amount in
amount * 0.9
}
static let fiveDollarsOff = Self { amount in
amount - 5
}
}
但是,我想知道如何 合并 这些折扣的多个。这是我的初始草图:
static func combine(_ discounts: [Discounting<A>]) -> Discounting<A> {
Discounting { (amount: A) -> Double in
return discounts.reduce(0.0) { current, discount in
// ??
}
}
}
但是,我不确定在 reduce 闭包中放什么。
如何将多个 Discounting
类型合并为一个?
使用此设计,您无法为任意 A
.
编写 Discounting<A>
的列表
A Discounting<A>
表示给定 A
对象计算折扣后价格的方法。请注意,这是 A
的函数,而不是价格的函数。从链接的文章中,此类型参数 A
似乎代表您正在打折的东西。
所以基本上,[Discounting<A>]
包含的信息是一个功能列表,给定一个东西 A
,可以为您提供它们的折扣价。如您所见,没有“申请另一个折扣”的余地。应用第一次折扣后,您得到的只是折扣后的价格,但 Discounting
代表 things 的折扣,而不是价格。您需要 discounted A
对象才能应用第二次折扣。
如果你有 Discounting<Double>
但是,组合是可能的,
func combine(_ discounts: [Discounting<Double>]) -> Discounting<Double> {
Discounting(discount: discounts.reduce({ [=10=] }, { compose([=10=], .discount) }))
}
func compose<T, U, V>(_ f1: @escaping (T) -> U, _ f2: @escaping (U) -> V) -> ((T) -> V) {
{ f2(f1([=10=])) }
}
为了解决一般情况下的问题,Discounting<A>
可以重新设计为返回输入的折扣版本:
struct Discounting<A> {
let discount: (A) -> A
}
// This is the "protocol witness" version of:
//
// protocol Discountable {
// func discount() -> Self
// }
这样,您还可以使用与我在上面显示的 Discounting<Double>
相同的代码来组合它们:
func combine<T>(_ discounts: [Discounting<T>]) -> Discounting<T> {
Discounting(discount: discounts.reduce({ [=12=] }, { compose([=12=], .discount) }))
}
用法示例:
struct Purchase {
var amount: Double
var shippingAmount: Double
}
extension Discounting where A == Purchase {
static let tenPercentOff: Self = .init { purchase in
Purchase(amount: purchase.amount * 0.9, shippingAmount: purchase.shippingAmount)
}
static let tenPercentOffShipping: Self = .init { purchase in
Purchase(amount: purchase.amount, shippingAmount: purchase.shippingAmount * 0.9)
}
static let fiveDollarsOff: Self = .init { purchase in
Purchase(amount: purchase.amount - 5, shippingAmount: purchase.shippingAmount)
}
}
let combinedDiscounts: Discounting<Purchase> = combine([.tenPercentOff, .fiveDollarsOff, .tenPercentOffShipping])
// Purchase(amount: 85.0, shippingAmount: 90.0)
print(combinedDiscounts.discount(Purchase(amount: 100, shippingAmount: 100)))
所以我一直在研究 protocol witness types,它们本质上是协议的具体实现。假设我们有一个对商品打折并返还双倍商品的协议。
而不是这个:
protocol Discountable {
func discount() -> Double
}
一个人这样做:
struct Discounting<A> {
let discount: (A) -> Double
}
并且,不是像这样将一种类型一次符合 Discountable
协议:
extension Double: Discountable {
func discount() -> Double {
return self * 0.9
}
}
可以为一个类型提供多个具体实现:
extension Discounting where A == Double {
static let tenPercentOff = Self { amount in
amount * 0.9
}
static let fiveDollarsOff = Self { amount in
amount - 5
}
}
但是,我想知道如何 合并 这些折扣的多个。这是我的初始草图:
static func combine(_ discounts: [Discounting<A>]) -> Discounting<A> {
Discounting { (amount: A) -> Double in
return discounts.reduce(0.0) { current, discount in
// ??
}
}
}
但是,我不确定在 reduce 闭包中放什么。
如何将多个 Discounting
类型合并为一个?
使用此设计,您无法为任意 A
.
Discounting<A>
的列表
A Discounting<A>
表示给定 A
对象计算折扣后价格的方法。请注意,这是 A
的函数,而不是价格的函数。从链接的文章中,此类型参数 A
似乎代表您正在打折的东西。
所以基本上,[Discounting<A>]
包含的信息是一个功能列表,给定一个东西 A
,可以为您提供它们的折扣价。如您所见,没有“申请另一个折扣”的余地。应用第一次折扣后,您得到的只是折扣后的价格,但 Discounting
代表 things 的折扣,而不是价格。您需要 discounted A
对象才能应用第二次折扣。
如果你有 Discounting<Double>
但是,组合是可能的,
func combine(_ discounts: [Discounting<Double>]) -> Discounting<Double> {
Discounting(discount: discounts.reduce({ [=10=] }, { compose([=10=], .discount) }))
}
func compose<T, U, V>(_ f1: @escaping (T) -> U, _ f2: @escaping (U) -> V) -> ((T) -> V) {
{ f2(f1([=10=])) }
}
为了解决一般情况下的问题,Discounting<A>
可以重新设计为返回输入的折扣版本:
struct Discounting<A> {
let discount: (A) -> A
}
// This is the "protocol witness" version of:
//
// protocol Discountable {
// func discount() -> Self
// }
这样,您还可以使用与我在上面显示的 Discounting<Double>
相同的代码来组合它们:
func combine<T>(_ discounts: [Discounting<T>]) -> Discounting<T> {
Discounting(discount: discounts.reduce({ [=12=] }, { compose([=12=], .discount) }))
}
用法示例:
struct Purchase {
var amount: Double
var shippingAmount: Double
}
extension Discounting where A == Purchase {
static let tenPercentOff: Self = .init { purchase in
Purchase(amount: purchase.amount * 0.9, shippingAmount: purchase.shippingAmount)
}
static let tenPercentOffShipping: Self = .init { purchase in
Purchase(amount: purchase.amount, shippingAmount: purchase.shippingAmount * 0.9)
}
static let fiveDollarsOff: Self = .init { purchase in
Purchase(amount: purchase.amount - 5, shippingAmount: purchase.shippingAmount)
}
}
let combinedDiscounts: Discounting<Purchase> = combine([.tenPercentOff, .fiveDollarsOff, .tenPercentOffShipping])
// Purchase(amount: 85.0, shippingAmount: 90.0)
print(combinedDiscounts.discount(Purchase(amount: 100, shippingAmount: 100)))