【问题标题】:Pass a type to a function to return a closure that returns that type?将类型传递给函数以返回返回该类型的闭包?
【发布时间】:2018-06-23 15:10:47
【问题描述】:

我一直在研究 Exercism.io 的 Swift 轨道,在将 BinarySearchTree 问题的解决方案重构为较小的代码后,我注意到一个模式似乎可以被重构。我认为答案是“不,你不能那样做”,但我认为值得一问。

typealias BinarySearchTree = BST

indirect enum BST< T: Comparable> {
    case N( T, BST, BST )
    case E

    init( _ v: T ) { self = .N( v, .E, .E ) }

    mutating func insert( _ n: T ) {
        if case .N( let v, var l, var r ) = self {
            if n <= v { l.insert( n ) } else { r.insert( n ) }
            self = .N( v, l, r )
        } else { self = .N( n, .E, .E ) }
    }

    var  data:    T?        { if case let .N( v, _, _ ) = self { return v } else { return nil } }
    var  left:    BST?      { if case let .N( _, l, _ ) = self { return l } else { return nil } }
    var  right:   BST?      { if case let .N( _, _, r ) = self { return r } else { return nil } }

    func allData() -> [ T ] { if case let .N( v, l, r ) = self { return l.allData() + [ v ] as [ T ] + r.allData() } else { return [ T ]() } }
}

我的重点是 vars dataleftright。看看他们是如何几乎吐出彼此的照片的?并不是说它应该完成,而是可以完成:编写一个函数,通过一个参数接受TBST,通过另一个参数(Int) 接受一个元组位置,并返回一个枚举分解并返回传入类型的捕获值的函数或闭包?

我意识到我的代码的短名称和单行性远远超出了愚蠢的程度。它是在对这个初始解决方案进行几次迭代后出现的,我想你会同意它更具可读性,尽管模式不太明显:

indirect enum BinarySearchTree< T: Comparable> {
    case Node( T, BinarySearchTree, BinarySearchTree )
    case None

    init( _ value: T ) { self = .Node( value, .None, .None ) }

    mutating func insert( _ new: T ) {
        if case .Node( let value, var left, var right ) = self {
            if new <= value { left.insert( new ) }
            else { right.insert( new ) }

            self = .Node( value, left, right )
        } else { self = .Node( new, .None, .None ) }
    }

    var data: T? {
        if case let .Node( value, _, _ ) = self { return value }
        else { return nil }
    }

    func allData() -> [ T ] {
        if case let .Node( value, left, right ) = self {
            return left.allData() + [ value ] as [ T ] + right.allData()
        } else { return [ T ]() }
    }

    var left: BinarySearchTree? {
        if case let .Node( _, left, _ ) = self { return left }
        else { return nil }
    }

    var right: BinarySearchTree? {
        if case let .Node( _, _, right ) = self { return right }
        else { return nil }
    }
}

【问题讨论】:

    标签: swift types


    【解决方案1】:

    这完全可以通过辅助函数来简化,并且可能应该简化:

    private var vlr: (T, BST, BST)? {
        switch self {
        case let .N(v, l, r): return (v,l,r)
        case .E: return nil
        }
    }
    
    var  data:  T?   { return vlr?.0 }
    var  left:  BST? { return vlr?.1 }
    var  right: BST? { return vlr?.2 }
    

    我在枚举上构建这些类型的帮助程序,以始终将关联数据转换为 Optionals(在某些情况下作为公共方法)。

    这也可以在allData 中使用,我可能会这样写(链式+ 因在 Swift 中创建编译问题而臭名昭著,并且由于创建中间副本而导致运行时效率极低):

    func allData() -> [ T ] {
        if let (v, l, r) = vlr {
            var result = l.allData()
            result.append(v)
            result.append(contentsOf: r.allData())
            return result
        }
        return []
    }
    

    【讨论】:

    • 太棒了!我想这是一个过于接近问题的案例。我无法看清类型以意识到我可以将它们全部返回,并从中选择我需要的东西,一个元组。谢谢。我讨厌看到可以像您展示的那样封装的重复代码。在allData() 中的链式+s 上,我知道你提到的性能问题。我想这就是为什么我需要投 [ value ] as [ T ] 才能让它工作,也是。我有一个稍微不那么简洁的版本,它在一次迭代中使用了.append(),直到我把它扔进了链接的+ 版本中。
    • 是的,我不知道为什么+ 需要as [T]。这是所有疯狂的+ 重载的奇怪副作用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-09
    • 2021-04-02
    • 1970-01-01
    • 1970-01-01
    • 2015-11-23
    • 2011-02-20
    相关资源
    最近更新 更多