【问题标题】:Global function sequence(state:next:) and type inference全局函数序列(state:next:) 和类型推断
【发布时间】:2017-03-05 06:03:32
【问题描述】:

背景和细节

Swift进化提案SE-0094在Swift 3.0中实现,引入全局sequence函数:

后者声明如下

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

并在swift/stdlib/public/core/UnfoldSequence.swift 中实现。语言参考给出了使用它的以下示例(注意缺少显式类型注释)

// Interleave two sequences that yield the same element type
sequence(state: (false, seq1.makeIterator(), seq2.makeIterator()), next: { iters in
  iters.0 = !iters.0
  return iters.0 ? iters.1.next() : iters.2.next()
})

但是,我无法让上面的示例正常工作(例如,使用 let seq1 = 1...3let seq2 = 4...6),但会提示相当奇怪的错误消息

错误:对成员“sequence(first:next:)”的引用不明确

只有当我在 next 闭包中显式键入注释可变的 State 参数以及它的返回类型时,上面的示例才会编译

let seq1 = 1...3
let seq2 = 4...6

for i in sequence(state: (false, seq1.makeIterator(), seq2.makeIterator()), 
        next: { (iters: inout (Bool, ClosedRangeIterator<Int>, ClosedRangeIterator<Int>)) 
                        -> Int? in
    iters.0 = !iters.0
    return iters.0 ? iters.1.next() : iters.2.next()
}) {
    print(i)
} // 1 4 2 5 3 6

然而,这不是我希望使用 sequence(state:next:) 的方式,因为我宁愿在类型推断正常工作的即时应用程序中看到它,避免所有的显式性。

问题

  • 我们是否打算将sequence(first:next:) 函数与上面的显式类型注释一起使用?由于inout 参数闭包,此功能是否存在一些限制,还是我遗漏了什么?

【问题讨论】:

  • @Hamish 谢谢,该错误报告将符合此行为。感谢关于通用专业化的另一点,只是在删除该点的过程中(由于其他原因)!至少我猜是裁判。 sequence(state:next:) 的文档应该更新以包含 inout 参数的显式注释,如果只是依靠文档(而不是广泛的 SO 贡献者知识!),尝试使用该函数会非常混乱。
  • @Hamish(如果你愿意的话)你可能会在你的评论中发布信息作为答案,它似乎涵盖了我的问题中解释“限制”询问的大部分内容。
  • 我已经继续并发布了一个答案 :) 虽然不幸的是,除了我在原始评论中所说的内容之外,我真的无法发现其他内容。
  • 我的书apeth.com/swiftBook/ch05.html#_for_loops 的这一部分末尾有一个斐波那契实现,我必须使用显式类型注释。但我并不介意。 :) [顺便说一句,我编辑了你的标题,因为结果是 q。真的只是类型推断问题]
  • @matt 感谢您的编辑和回答!

标签: swift swift3 sequence


【解决方案1】:

这看起来像是两个问题的结合。

首先是 Swift 目前不会在没有任何外部上下文的情况下推断多行闭包的类型。然而,这是预期的行为,正如 Apple 开发人员 Jordan Rose 在SR-1570 的 cmets 中所证实的那样:

这是正确的行为:Swift 不会从多语句闭包的主体中推断参数或返回类型。但诊断可能会好很多。

因此理论上,你只需要显式定义你传递给sequence()next:参数的闭包的返回类型,因为参数类型可以从外部上下文中推断出来(即你传入的类型state: 参数):

let seq1 = 1...3
let seq2 = 4...6

let combined = sequence(state: (false, seq1.makeIterator(), seq2.makeIterator()),
                        next: { iters -> Int? in
    iters.0 = !iters.0
    return iters.0 ? iters.1.next() : iters.2.next()
})

(编辑:现在可以在 Swift 3.1 中编译)


但是,这仍然无法编译 - 这是由于第二个问题,编译器无法推断 Swift 3 中 inout 闭包参数的类型(在 Swift 2 中不是这种情况)。这是一个可疑的错误,已提交(参见SR-1976SR-1811)。

因此,正如您在问题中所指出的,这意味着(非常不令人满意)您必须明确注释您传递给 next: 的完整闭包签名:

let combined = sequence(state: (false, seq1.makeIterator(), seq2.makeIterator()),
                        next: { (iters: inout (Bool, ClosedRangeIterator<Int>, ClosedRangeIterator<Int>)) -> Int? in
    iters.0 = !iters.0
    return iters.0 ? iters.1.next() : iters.2.next()
})

【讨论】:

  • 另外,我绝对建议向 Apple 提交一个关于无法编译的文档示例的错误。
  • 好的,但让我们公平一点。您可以使用类型别名和尾随闭包语法,并使代码看起来 lot 更好。你有点故意让它看起来很糟糕。
  • @matt 我同意尾随闭包语法可以使它看起来稍微好一点(尽管在这种情况下我实际上有点偏爱使用 next: 参数名称,因为(至少对我而言)它增加了清晰度,并使 Xcode 与 state: 参数很好地对齐)。如果您要使用相同的State 类型多次调用sequence(state:next:)typealias 是个好主意,尽管如果只使用一次,IMO 似乎有些多余。
  • 哦,我同意这是我们不应该做的事情。
【解决方案2】:

但即使承认这是一个错误,您仍然可以让代码看起来更好,而不用担心它(在浏览器中输入而无需测试,但这样的事情应该可以工作):

typealias MyTriple = (Bool, ClosedRangeIterator<Int>, ClosedRangeIterator<Int>)
let someTriple : MyTriple = (false, seq1.makeIterator(), seq2.makeIterator())
let combined = sequence(state: someTriple) { 
    (iters: inout MyTriple) -> Int? in
    iters.0 = !iters.0
    return iters.0 ? iters.1.next() : iters.2.next()
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-08
    • 2021-03-09
    • 1970-01-01
    • 2016-01-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多