带有增量的 Swift 3 for 循环

Posted

技术标签:

【中文标题】带有增量的 Swift 3 for 循环【英文标题】:Swift 3 for loop with increment 【发布时间】:2016-09-07 07:14:03 【问题描述】:

如何在 Swift3 中编写以下代码?

for (f = first; f <= last; f += interval)          

    n += 1

这是我自己的尝试

for _ in 0.stride(to: last, by: interval)

    n += 1

【问题讨论】:

【参考方案1】:

for _ in 0.stride(to: last, by: interval) n += 1

【讨论】:

你不能在 Swift 3 中说 0.stride【参考方案2】:

Swift 2.2 -> 3.0: Strideable:s stride(...) 替换为全局 stride(...) 函数

在 Swift 2.2 中,我们可以(正如您自己尝试过的那样)利用蓝图(和默认实现)函数 stride(through:by:)stride(to:by:) from the protocol Strideable

/* Swift 2.2: stride example usage */
let from = 0
let to = 10
let through = 10
let by = 1
for _ in from.stride(through, by: by)   // from ... through (steps: 'by')
for _ in from.stride(to, by: by)        // from ..< to      (steps: 'by')

而在 Swift 3.0 中,这两个函数已从 Strideable 中删除,取而代之的是 the global functions stride(from:through:by:) and stride(from:to:by:);因此上述等效的 Swift 3.0 版本如下

/* Swift 3.0: stride example usage */
let from = 0
let to = 10
let through = 10
let by = 1
for _ in stride(from: from, through: through, by: by)  
for _ in stride(from: from, to: to, by: by)  

在您的示例中,您希望使用闭区间步幅替代 stride(from:through:by:),因为您的 for 循环中的不变量使用与小于 或等于 (&lt;=) 的比较。即

/* example values of your parameters 'first', 'last' and 'interval' */
let first = 0
let last = 10
let interval = 2
var n = 0
for f in stride(from: first, through: last, by: interval)  
    print(f)
    n += 1 
 // 0 2 4 6 8 10
print(n) // 6

当然,我们仅使用您的 for 循环作为从 for 循环到 stride 的段落的示例,您可以自然地,对于您的具体示例,只需计算 n 而无需一个循环 (n=1+(last-first)/interval)。

Swift 3.0:stride 的替代方案,用于更复杂的迭代递增逻辑

随着the implementation of evolution proposal SE-0094,Swift 3.0 引入了全局sequence 函数:

sequence(first:next:), sequence(state:next:),

对于具有更复杂的迭代增量关系的情况(本例中不是这种情况),它可以是stride 的适当替代方案。

声明

func sequence<T>(first: T, next: @escaping (T) -> T?) -> 
         UnfoldSequence<T, (T?, Bool)>

func sequence<T, State>(state: State, 
                        next: @escaping (inout State) -> T?) ->
           UnfoldSequence<T, State>

我们将简要介绍这两个函数中的第一个。 next 参数采用一个闭包,该闭包应用一些逻辑来懒惰地构造给定当前元素的下一个序列元素(以first 开头)。当next 返回nil 时序列终止,如果next 永远不会返回nil,则序列终止。

应用于上面简单的常量步幅示例,sequence 方法有点冗长和过度杀伤 w.r.t。适合此用途的stride 解决方案:

let first = 0
let last = 10
let interval = 2
var n = 0
for f in sequence(first: first,
                  next:  $0 + interval <= last ? $0 + interval : nil ) 
    print(f)
    n += 1
 // 0 2 4 6 8 10
print(n) // 6

sequence 函数对于步幅不恒定的情况非常有用,但是,例如如以下问答中的示例所示:

Express for loops in swift with dynamic range

只需注意以最终的nil 返回来终止序列(如果不是:“无限”元素生成),或者,当 Swift 3.1 到来时,结合使用其惰性生成与 prefix(while:) 方法序列,如进化提案SE-0045 中所述。后者应用于此答案的运行示例使sequence 方法不那么冗长,显然包括元素生成的终止标准。

/* for Swift 3.1 */
// ... as above
for f in sequence(first: first, next:  $0 + interval )
    .prefix(while:  $0 <= last ) 
    print(f)
    n += 1
 // 0 2 4 6 8 10
print(n) // 6

【讨论】:

我们不是在 Swift 2 中远离全局函数,而在 Swift 3 中回归它们吗? > 新版本的 swift 中的 stride 是什么?【参考方案3】:

Swift 3.0 的简单工作代码:

let (first, last, interval) = (0, 100, 1)
var n = 0
for _ in stride(from: first, to: last, by: interval) 
    n += 1

【讨论】:

如果你正在遍历一个数组并且也想要一个索引,你可以使用enumerated(),如下所示:***.com/a/24028458/8047【参考方案4】:

使用 Swift 5,您可以选择以下 5 个示例之一来解决您的问题。


#1。使用stride(from:to:by:)函数

let first = 0
let last = 10
let interval = 2

let sequence = stride(from: first, to: last, by: interval)

for element in sequence 
    print(element)


/*
prints:
0
2
4
6
8
*/

#2。使用sequence(first:next:)函数

let first = 0
let last = 10
let interval = 2

let unfoldSequence = sequence(first: first, next: 
    $0 + interval < last ? $0 + interval : nil
)

for element in unfoldSequence 
    print(element)


/*
prints:
0
2
4
6
8
*/

#3。使用 AnySequence init(_:) 初始化器

let anySequence = AnySequence<Int>( () -> AnyIterator<Int> in
    let first = 0
    let last = 10
    let interval = 2

    var value = first
    return AnyIterator<Int> 
        defer  value += interval 
        return value < last ? value : nil
    
)

for element in anySequence 
    print(element)


/*
prints:
0
2
4
6
8
*/

#4。使用CountableRangefilter(_:)方法

let first = 0
let last = 10
let interval = 2

let range = first ..< last
let lazyCollection = range.lazy.filter( $0 % interval == 0 )

for element in lazyCollection 
    print(element)


/*
prints:
0
2
4
6
8
*/

#5。使用CountableRangeflatMap(_:)方法

let first = 0
let last = 10
let interval = 2

let range = first ..< last
let lazyCollection = range.lazy.compactMap( $0 % interval == 0 ? $0 : nil )

for element in lazyCollection 
    print(element)


/*
prints:
0
2
4
6
8
*/

【讨论】:

【参考方案5】:

我们也可以使用while循环作为替代方式

while first <= last 
    first += interval

【讨论】:

以上是关于带有增量的 Swift 3 for 循环的主要内容,如果未能解决你的问题,请参考以下文章

V for 循环增量

在 For 循环增量中使用 if else

如何在 C++ 'for' 循环中放置两个增量语句?

为啥'continue'语句忽略'while'循环中的循环计数器增量,而不是'for'循环?

R中的for循环,增量

for循环不迭代所有增量