【问题标题】:Swift Problem with Composing Lazy Sequence Transforms组合惰性序列变换的 Swift 问题
【发布时间】:2019-02-26 08:46:29
【问题描述】:

我将继续涉足功能性 swift 并非常享受挑战。 我正在使用将元素转换为惰性序列的转换。

为了预先说明错误,我得到: 无法转换类型的值 '变换'(又名'(Int)-> LazySequence>') 到预期的参数类型 '() -> LazySequence]>'

我的问题在于编写它们,但我需要提供一些上下文来说明问题。

这是变换:

typealias Transform<T, U> = (T) -> LazySequence<[U]>

我可以定义前向应用:

precedencegroup LazyForwardApplication {
  associativity: left
}

infix operator |~>: LazyForwardApplication

func |~> <T: LazySequenceProtocol, U>(
  input: T,
  transform: @escaping Transform<T.Elements.Element,U>
  ) -> LazySequence<FlattenSequence<LazyMapSequence<T.Elements, LazySequence<[U]>>>> {

  return input.flatMap(transform)
}

返回类型有点拗口,但效果很好:

let start = [10,20,30].lazy

let add4_5_6: Transform<Int, Int> = {
  let result = [ $0 + 4, $0 + 5, $0 + 6]
  print("> add4_5_6(\($0)) -> \(result)")
  return result.lazy
}

// 请注意,我将调试部分放入,因此我可以确定它是延迟发生的。

let result1 = start |~> add4_5_6
result1.forEach{ print($0) }
// 14, 15, 16, 24, 25, 26, 34, 35, 36

还有一个类似的例子:

let add7000_8000: Transform<Int, Int> = {
  let result = [ $0 + 7000, $0 + 8000]
  print("> add7000_8000(\($0)) -> \(result)")
  return result.lazy
}

let result2 = start |~> add7000_8000
result2.forEach{ print($0) }
// 7010, 8010, 7020, 8020, 7030, 8030

我可以将它们串联在一起:

// Double application
let result3 = start |~> add4_5_6 |~> add7000_8000
result3.forEach{ print($0) }
// 7014, 8014, 7015, 8015, 7016, 8016,
// 7024, 8024, 7025, 8025, 7026, 8026,
// 7034, 8034, 7035, 8035, 7036, 8036

但我也希望能够创作它们:

// Forward Composition
precedencegroup LazyForwardComposition {
  associativity: right
}
infix operator >~>: LazyForwardComposition

func >~> <T, U: Sequence, V: Sequence>(
  left:  @escaping Transform<T,U>,
  right: @escaping Transform<U,V>
  ) -> (T) -> LazySequence<FlattenSequence<LazyMapSequence<[U], LazySequence<[V]>>>> {

  return { input in
    let b: LazySequence<[U]> = left(input)
    let c = b.flatMap(right)
    return c
  }
}

这是我得到错误的地方:

let composed = add4_5_6 >~> add7000_8000
// ERROR IN ABOVE LINE: Cannot convert value of type
'Transform<Int, Int>' (aka '(Int) -> LazySequence<Array<Int>>')
to expected argument type
'(_) -> LazySequence<[_]>'

let result4 = start |~> composed
result4.forEach{ print($0) }

结果会和 result3 一样

我已经解决了几次这个问题,但一直卡住。 任何关于如何解决的想法表示赞赏。

(我之前的问题是类似的领域,但不同的问题: Swift: Lazily encapsulating chains of map, filter, flatMap )

游乐场:

typealias Transform<T, U> = (T) -> LazySequence<[U]>

// And I can define Forward Application:

precedencegroup LazyForwardApplication {
  associativity: left
}

infix operator |~>: LazyForwardApplication

func |~> <T: LazySequenceProtocol, U>(
  input: T,
  transform: @escaping Transform<T.Elements.Element,U>
  ) -> LazySequence<FlattenSequence<LazyMapSequence<T.Elements, LazySequence<[U]>>>> {

  return input.flatMap(transform)
}

// The return type is a bit of a mouthful but it works fine:

let start = [10,20,30].lazy

let add4_5_6: Transform<Int, Int> = {
  let result = [ $0 + 4, $0 + 5, $0 + 6]
  print("> add4_5_6(\($0)) -> \(result)")
  return result.lazy
}

// Note that I put the debug in partly so I can be sure that it's happening lazily.

let result1 = start |~> add4_5_6
result1.forEach{ print($0) }
// 14, 15, 16, 24, 25, 26, 34, 35, 36

// And another similar example:

let add7000_8000: Transform<Int, Int> = {
  let result = [ $0 + 7000, $0 + 8000]
  print("> add7000_8000(\($0)) -> \(result)")
  return result.lazy
}

let result2 = start |~> add7000_8000
result2.forEach{ print($0) }
// 7010, 8010, 7020, 8020, 7030, 8030

// And I can chain these together inline: 

// Double application
let result3 = start |~> add4_5_6 |~> add7000_8000
result3.forEach{ print($0) }
// 7014, 8014, 7015, 8015, 7016, 8016,
// 7024, 8024, 7025, 8025, 7026, 8026,
// 7034, 8034, 7035, 8035, 7036, 8036

// But I'd like to be able to compose them too:

// Forward Composition
precedencegroup LazyForwardComposition {
  associativity: right
}
infix operator >~>: LazyForwardComposition

func >~> <T, U: Sequence, V: Sequence>(
  left:  @escaping Transform<T,U>,
  right: @escaping Transform<U,V>
  ) -> (T) -> LazySequence<FlattenSequence<LazyMapSequence<[U], LazySequence<[V]>>>> {

  return { input in
    let b: LazySequence<[U]> = left(input)
    let c = b.flatMap(right)
    return c
  }
}

// And here's where I get an error:

let composed = add4_5_6 >~> add7000_8000
// ERROR IN ABOVE LINE: Cannot convert value of type 'Transform<Int, Int>' (aka '(Int) -> LazySequence<Array<Int>>') to expected argument type '(_) -> LazySequence<[_]>'

let result4 = start |~> composed
result4.forEach{ print($0) }

// The result would come out the same as result3

【问题讨论】:

    标签: swift functional-programming lazy-sequences


    【解决方案1】:

    我有一个部分答案,但也许它可以帮助你到达某个地方。

    首先Transform&lt;T, U&gt;被定义为(T) -&gt; LazySequence&lt;[U]&gt;,所以UV泛型类型不能特化为Sequence

    func >~> <T, U, V>(
        left:  @escaping Transform<T,U>,
        right: @escaping Transform<U,V>
        ) -> (T) -> LazySequence<FlattenSequence<LazyMapSequence<[U], LazySequence<[V]>>>> {
    
        return { input in
            let b = left(input)
            let c = b.flatMap(right)
            return c
        }
    }
    

    其次,您的|~&gt; 运算符接受Transform 作为右手参数,因此您不能将它与&gt;~&gt; 参数的返回类型一起使用。我能够通过以下行得到结果:

    let result4 = start.flatMap(composed)
    

    您可能会重载|~&gt; 运算符以接受正确的类型,但它看起来不太好。或者也许它会,有足够的类型别名:)

    【讨论】:

    • 谢谢,@pckill。我可以看到为什么序列专业化需要删除并且您的其他 cmets 很有用。当我解决这个问题时,我会回复。
    猜你喜欢
    • 1970-01-01
    • 2011-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多