SequenceType
是生成一系列值的一种有用的方法,并且使其对这些值进行迭代特别有用。我对这些
SequenceType
类型的经验并不多,所以我想实现我自己进行一些练习和学习。是什么比Fizz Buzz序列更好看的序列,对吗?我想让这个Fizz Buzz有点特殊。我希望用户定义任何种类的规则,并根据需要添加尽可能多的测试。我们只需将每个测试与一个单词配对,并传递这些测试词对的数组,然后让序列完成所有工作。
因此,首先,我为“测试”创建自定义类型”和测试字“ Pair”:
typealias FizzBuzzRule = (Int) -> Bool
typealias FizzBuzzPair = (test: FizzBuzzRule, word: String)
因此,使用普通的FizzBuzz示例,我们将像这样创建普通的“ Fizz”和“ Buzz”测试:
let fizzTest = { (i: Int) -> Bool in
return i % 3 == 0
}
let buzzTest = { (i: Int) -> Bool in
return i % 5 == 0
}
let fizzPair: FizzBuzzPair = (fizzTest, "Fizz")
let buzzPair: FizzBuzzPair = (buzzTest, "Buzz")
let pairs = [fizzPair, buzzPair]
但是,当然,我们可以创建所需的任何规则。这些只是示例,我们将在其余代码中看到,使用这些示例规则将如何产生标准的“ FizzBuzz”问题结果。
下一步是编写函数以应用规则并产生所需的输出。为此,我编写了
fizzBuzzify
函数:func fizzBuzzify(value: Int, fizzBuzzPairs: [FizzBuzzPair]) -> String {
var retnValue: String? = nil
for pair in fizzBuzzPairs {
if pair.test(value) {
retnValue = (retnValue ?? "") + pair.word
}
}
return retnValue ?? String(value)
}
所以现在,我们可以传递任何值和任何Test-Word对数组,并构建FizzBuzz类型的字符串只需使用此功能即可。
已经可以执行以下操作:
for x in 1...100 {
println(fizzBuzzify(value, pairs))
}
但是,我想更进一步,并将其改进为为我们生成值的序列,因此我需要将
FizzBuzzSequence
创建为SequenceType
:我们将它们放在一起,可以简单地用作:struct FizzBuzzSequence: SequenceType {
let startValue: Int
let endValue: Int
let pairs: [FizzBuzzPair]
init(start: Int = 1, end: Int = 100, pairs: [FizzBuzzPair]) {
self.startValue = start
self.endValue = end
self.pairs = pairs
}
init(start: Int = 1, end: Int = 100, pairs: FizzBuzzPair...) {
self.init(start: start, end: end, pairs: pairs)
}
func generate() -> GeneratorOf<String> {
var value: Int = self.startValue
return GeneratorOf<String> {
return (value <= self.endValue) ? fizzBuzzify(value++, self.pairs) : nil
}
}
}
并且假设
pairs
与FizzBuzzPair
的数组相同我们之前设置的结果将与您希望看到的任何其他FizzBuzz程序完全相同。但是我们现在可以从任何值开始,以任何值结束,并设置我们想要的任何规则。
我正在寻找有关此代码快速性的一般注释,以及对代码效率的双重检查。该程序一般。我什至还在使用
SequenceType
的预期用途? for fizzBuzzValue in FizzBuzzSequence(start: 1, end: 100, pairs: pairs) {
println(fizzBuzzValue)
}
#1 楼
您的代码对我来说看起来不错,我只有一些小小的注释和建议。 br />可以简化为
var retnValue: String? = nil
在
var retnValue: String?
类型注释
nil
不需要第一个: Int
,可以在第二种情况下从编译器推断通用类型
self
:func generate() -> GeneratorOf<String> {
var value: Int = self.startValue
return GeneratorOf<String> {
return (value <= self.endValue) ? fizzBuzzify(value++, self.pairs) : nil
}
}
命名函数“ fizzTest”,“ buzzTest”的定义,闭包可以用来定义测试,整个规则集可以写成
func generate() -> GeneratorOf<String> {
var value = startValue
return GeneratorOf {
return (value <= self.endValue) ? fizzBuzzify(value++, self.pairs) : nil
}
}
<String>
仅在一个地方使用,因此您可以删除它并定义
let pairs : [FizzBuzzPair] = [
( { typealias FizzBuzzPair = (test: (Int) -> Bool, word: String)
% 3 == 0 } , "Fizz"),
( { struct FizzBuzzRule {
let test : (Int) -> Bool
let word : String
}
let rules = [
FizzBuzzRule(test: { let fbSequence = lazy(1 ... 100).map { fizzBuzzify(q4312078q, pairs) }
for fbValue in fbSequence {
println(fbValue)
}
% 3 == 0 }, word: "Fizz") ,
FizzBuzzRule(test: { q4312078q % 5 == 0 }, word: "Buzz")
]
% 5 == 0 } , "Buzz")
]
。我可能会定义一个
typealias FizzBuzzRule
而不是一个元组:q4312078q
,因为编译器诊断和自动补全对struct更好, 。
最后请注意,
struct
本质上是进行从整数范围到字符串序列的映射,因此使用
可以实现同样的效果。 > q4312078q
评论
\ $ \ begingroup \ $
是否有任何时间首选映射到SequenceType或反之?这是一种口味问题,还是有些时候另一种实际上更好?
\ $ \ endgroup \ $
– nhgrif
15年5月25日在11:44
\ $ \ begingroup \ $
@nhgrif:各种Lazy ...类型的map()方法也返回一个序列,因此fbSequence是一个序列。这可能只是一个口味问题,我只是觉得这种方法可以更好地分离关注点(枚举与映射)。
\ $ \ endgroup \ $
–马丁R
15年5月25日在12:01