Swift的SequenceType是生成一系列值的一种有用的方法,并且使其对这些值进行迭代特别有用。

我对这些SequenceType类型的经验并不多,所以我想实现我自己进行一些练习和学习。是什么比Fizz Buzz序列更好看的序列,对吗?

我想让这个Fizz Buzz有点特殊。我希望用户定义任何种类的规则,并根据需要添加尽可能多的测试。我们只需将每个测试与一个单词配对,并传递这些测试词对的数组,然后让序列完成所有工作。

因此,首先,我为“测试”创建自定义类型”和测试字“ Pair”:

typealias FizzBuzzRule = (Int) -> Bool
typealias FizzBuzzPair = (test: FizzBuzzRule, word: String)


因此,使用普通的FizzBu​​zz示例,我们将像这样创建普通的“ 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]



但是,当然,我们可以创建所需的任何规则。这些只是示例,我们将在其余代码中看到,使用这些示例规则将如何产生标准的“ FizzBu​​zz”问题结果。

下一步是编写函数以应用规则并产生所需的输出。为此,我编写了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对数组,并构建FizzBu​​zz类型的字符串只需使用此功能即可。

已经可以执行以下操作:


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
        }
    }
}



并且假设pairsFizzBuzzPair的数组相同我们之前设置的结果将与您希望看到的任何其他FizzBu​​zz程序完全相同。

但是我们现在可以从任何值开始,以任何值结束,并设置我们想要的任何规则。

我正在寻找有关此代码快速性的一般注释,以及对代码效率的双重检查。该程序一般。我什至还在使用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