我正在尝试学习Swift功能并开始从Project Euler做一些练习。
甚至斐波那契数字 问题2 Fibonacci序列中的每个新术语都是通过添加前两个术语生成的。从1和2开始,前10个术语将是:
1,2,3,5,8,13,21,34,55,89,......
通过考虑Fibonacci序列中的值不超过四百万的项,找到偶数项的总和。
根据WWDC高级Swift视频实现了一个memoized Fibonacci函数:
func memoize<T:Hashable, U>( body: ((T)->U,T) -> U) -> (T)->U {
var memo = [T:U]()
var result: ((T)->U)!
result = { x in
if let q = memo[x] { return q }
let r = body(result,x)
memo[x] = r
return r
}
return result
}
let fibonacci = memoize { (fibonacci:Int->Double,n:Int) in n < 2 ? Double(n) : fibonacci(n-1) + fibonacci(n-2) }
并实现了符合Sequence
协议
class FibonacciSequence: SequenceType {
func generate() -> GeneratorOf<Double> {
var n = 0
return GeneratorOf<Double> { fibonacci(n++) }
}
subscript(n: Int) -> Double {
return fibonacci(n)
}
}
问题的第一个(非功能性)解决方案:
var fib = FibonacciSequence().generate()
var n:Double = 0
var sum:Double = 0
while n < Double(4_000_000) {
if n % 2 == 0 {
sum += n
}
n = fib.next()!
}
println(sum)
第二个更具功能性的解决方案,使用ExSwift进行takeWhile
功能
let f = FibonacciSequence()
println((1...40).map { f[$0] }
.filter { $0 % 2 == 0 }
.takeWhile { $0 < 4_000_000 }
.reduce(0, combine: +))
我想改进这个解决方案,因为乞讨的1...40
范围无缘无故地计算了太多的术语。理想情况下,我希望能够拥有某种无限范围,但同时只计算满足takeWhile
有什么建议吗?
答案 0 :(得分:3)
这里我生成一旦达到最大值就已经停止的序列。
然后你只需要在没有过滤的情况下进行缩减,只需在n
为奇数时加0。
func fibonacciTo(max: Int) -> SequenceOf<Int> {
return SequenceOf { _ -> GeneratorOf<Int> in
var (a, b) = (1, 0)
return GeneratorOf {
(b, a) = (a, b + a)
if b > max { return nil }
return b
}
}
}
let sum = reduce(fibonacciTo(4_000_000), 0) {a, n in (n % 2 == 0) ? a + n : a }
作为替代方案,如果您希望fibonacci
保持SequenceOf
更通用的功能,可以takeWhile
reduce1
延长 extension SequenceOf {
func takeWhile(p: (T) -> Bool) -> SequenceOf<T> {
return SequenceOf { _ -> GeneratorOf<T> in
var generator = self.generate()
return GeneratorOf {
if let next = generator.next() {
return p(next) ? next : nil
}
return nil
}
}
}
// Reduce1 since name collision is not resolved
func reduce1<U>(initial: U, combine: (U, T) -> U) -> U {
return reduce(self, initial, combine)
}
}
func fibonacci() -> SequenceOf<Int> {
return SequenceOf { _ -> GeneratorOf<Int> in
var (a, b) = (1, 0)
return GeneratorOf {
(b, a) = (a, b + a)
return b
}
}
}
let sum2 = fibonacci()
.takeWhile({ $0 < 4_000_000 })
.reduce1(0) { a, n in (n % 2 == 0) ? a + n : a}
,// Get data
$query = "SELECT * FROM games WHERE player = '$thePlayer' ORDER BY whenPlayed";
$result = $mysqli->query($query);
$data = array();
// Iterate over data, use 'whenPlayed` as a key to group your data
while ($row = $result->fetch_array(MYSQLI_ASSOC)) {
if (!array_key_exists($row['whenPlayed'], $data)) {
$data[$row['whenPlayed']] = array();
}
$data[$row['whenPlayed']][] = $row;
}
获取类似的东西功能组成:
<ul>
<?php foreach ($data as $date => $games): ?>
<li>
<?php echo $date ?>:
<ul>
<?php foreach ($games as $game): ?>
<li><?php echo print_r($game, true) ?></li>
<?php endforeach ?>
</ul>
</li>
<?php endforeach ?>
</ul>
希望这有帮助
答案 1 :(得分:2)
有一个filter()
函数,它将序列作为参数:
func filter<S : SequenceType>(source: S, includeElement: (S.Generator.Element) -> Bool) -> [S.Generator.Element]
但由于返回值是数组,如果您愿意,这不适用 使用“无限”序列。但是
lazy(FibonacciSequence()).filter ( { $0 % 2 == 0 })
你得到了一个偶数斐波纳契数的“无限”序列。你不能
在该序列上调用ExSwift的.takeWhile()
方法,因为
.takeWhile()
仅定义为struct SequenceOf
,而非TakeWhileSequence(
lazy(FibonacciSequence()).filter ( { $0 % 2 == 0 }),
{ $0 < 4_000_000 }
)
一般序列。但
let sum = reduce(TakeWhileSequence(
lazy(FibonacciSequence()).filter ( { $0 % 2 == 0 }),
{ $0 < 4_000_000 }), 0, +)
工作并给出所有偶数Fibonacci数小于的序列 4000000。然后
struct FibonacciSequence : SequenceType {
func generate() -> GeneratorOf<Int> {
var current = 1
var next = 1
return GeneratorOf<Int>() {
let result = current
current = next
next += result
return result
};
}
}
给出预期结果并仅计算“必要” 斐波那契数字。
请注意,实际上不需要记住Fibonacci数字 这是因为它们是按顺序访问的。另外(作为@Matteo 已经注意到了),所有Fibonacci数都是整数。所以你可以 将序列更简单地定义为
/\d+\/\d+\.\d+\.\d+|\d+\.\d+\.\d+|\d+/g
并且上述计算仍然有效。
答案 2 :(得分:1)
使用Swift的懒惰序列,你可以非常接近你想要的东西。如果你把你的斐波纳契数字生成器(这里是我用的那个):
var (a, b) = (1, 0)
var fibs = GeneratorOf<Int> {
(b, a) = (a, b + a)
return b
}
您可以将其包装在lazy()中:
var (a, b) = (1, 0)
var fibs = lazy(
GeneratorOf<Int> {
(b, a) = (a, b + a)
return b
}
)
将其公开为filter()作为一个惰性函数。这个filter()返回:
LazySequence<FilterSequenceView<GeneratorOf<Int>>>
现在,为了获得你的takeWhile()函数,你需要扩展LazySequence:
extension LazySequence {
func takeWhile(condition: S.Generator.Element -> Bool)
-> LazySequence<GeneratorOf<S.Generator.Element>> {
var gen = self.generate()
return lazy( GeneratorOf{ gen.next().flatMap{ condition($0) ? $0 : nil }})
}
}
如果基础序列结束,或者条件不满足,则返回nil(停止生成器)。
所有这些,你给定数字下的斐波纳契序列看起来很像你想要的:
fibs
.filter {$0 % 2 == 0}
.takeWhile {$0 < 100}
//2, 8, 34
但是,因为reduce不是LazySequence的方法,你必须转换为数组:
fibs
.filter {$0 % 2 == 0}
.takeWhile {$0 < 100}.array
.reduce(0, combine: +)
//44
你可以对LazySequence做一个快速而又脏的扩展来获得reduce():
extension LazySequence {
func reduce<U>(initial: U, combine: (U, S.Generator.Element) -> U) -> U {
var accu = initial
for element in self { accu = combine(accu, element) }
return accu
}
}
你可以写下这样的最终内容:
fibs
.filter {$0 % 2 == 0}
.takeWhile {$0 < 100}
.reduce(0, combine: +)
//44
所有这些序列都会在他们的懒惰中持续存在 - 纤维是无限的,所以他们不会在其他方面起作用。事实上,在减少之前不计算任何东西:直到那时它才是所有的thunk。
答案 3 :(得分:1)
在Swift 3.1中,这里是一个永远生成Fibonacci数的迭代器,以及从中得到的无限序列:
class FibIterator : IteratorProtocol {
var (a, b) = (0, 1)
func next() -> Int? {
(a, b) = (b, a + b)
return a
}
}
let fibs = AnySequence{FibIterator()}
你可以得到四百万以下偶数项的总和:
fibs.prefix{$0 < 4000000}.filter{$0 % 2 == 0}.reduce(0){$0 + $1}
请注意,默认情况下filter
和map
是严格的,并且会在无限序列上永久运行。在上面的示例中,这并不重要,因为prefix
仅返回有限数量的值。您可以致电.lazy
以获取惰性序列,其中filter
和map
的行为非严格。例如,这里是前5个甚至斐波纳契数:
> print( Array(fibs.lazy.filter{$0 % 2 == 0}.prefix(5)) )
[2, 8, 34, 144, 610]