当前位置:  开发笔记 > IOS > 正文

有没有办法在调用SequenceType.forEach时引用实例函数?

如何解决《有没有办法在调用SequenceType.forEach时引用实例函数?》经验,为你挑选了1个好方法。

考虑类型Foo:

class Foo {

    var isBaz: Bool {
        return false
    }

    func bar() {
        print("some boring print")
    }
}

现在让我们说我想遍历一个类实例集合并在每个类实例上调用一些函数:

let someFoos: [Foo] = [Foo(), Foo(), Foo()]

someFoos.forEach { $0.bar() }

这种语法非常紧凑,但感觉有点尴尬.此外,它无法在任何地方使用.例如,在if声明条件中:

if someFoos.contains { $0.isBaz } { 
    // compiler error: statement cannot begin with a closure expression
}

if someFoos.contains($0.isBaz) { 
    // compiler error: anonymous closure argument not contained in a closure
}

if someFoos.contains({ $0.isBaz }) { 
    // this is correct, but requires extra pair of parentheses
}

理想情况下,写一些类似的东西会很好

someFoos.forEach(Foo.bar)

但是从Swift 2.1开始,这不是一个正确的语法.这种引用函数的方式类似于以下内容:

func bar2(foo: Foo) -> Void {
    print("some boring print")
}

someFoos.forEach(bar2)

有没有更好的方法来引用实例函数?你更喜欢写这样的表达方式?



1> Martin R..:

这里有两个不同的问题.的拖尾闭合语法 可以在调用函数时,可以使用与最后一个参数是一个闭合,所以

let b1 = someFoos.contains({ $0.isBaz })
let b2 = someFoos.contains { $0.isBaz }

是完全相同的.但是,在if语句的条件下,尾随闭包语法可能会有问题:

if someFoos.contains({ $0.isBaz }) { }  // OK
if someFoos.contains { $0.isBaz } { }   // Compiler error
if (someFoos.contains { $0.isBaz }) { } // OK, as noted by R Menke

我们只能推测为什么第二个不起作用.可能是编译器将第一个{ 作为if-body的开头.也许这将在Swift的未来版本中发生变化,但可能不值得努力.


另一个问题是关于curried函数.

someFoos.forEach(bar2)

compiles因为bar2有类型Foo -> Void,这正是forEach()方法所期望的.Foo.bar,在另一方面,是咖喱功能(参见http://oleb.net/blog/2014/07/swift-instance-methods-curried-functions/)这需要实例作为第一个参数.它有类型Foo -> () -> ().所以

Foo.bar(someFoo)

是一个类型的闭包() -> (),和

Foo.bar(someFoo)()

barsomeFoo实例上调用该方法.

(注意:以下内容并不是一个实际的推荐,而只是作为关于curried函数和闭包乐趣的演示!)

Foo.bar直接作为参数传递,forEach()我们需要"交换"参数的顺序.Haskell为此目的有一个"翻转"函数,在Swift中也是可能的(参见例如如何在Swift 中编写一个翻转方法?):

func flip(f: A -> B ->C) -> B -> A ->C {
    return { b in { a in f(a)(b) } }
}

然后flip(Foo.bar)有类型() -> Foo -> (),因此bar可以应用方法的void参数

flip(Foo.bar)()

得到一个Foo -> ()关闭,和

flip(Foo.bar)()(someFoo)

barsomeFoo实例上调用该方法.现在我们可以打电话了

someFoos.forEach (flip(Foo.bar)())

不使用闭包表达式{ .. }!!

如果isBaz方法而不是属性

func isBaz() -> Bool { return false }

然后你可以在if-expression中做同样的事情:

if someFoos.contains(flip(Foo.isBaz)()) { 
    // ...
}

同样,这只是一个示范.同样性质 不是令行禁止的功能,所以这不能与您进行isBaz财产.


感谢您抽出宝贵时间撰写如此详尽的答案!很多值得思考的东西!
推荐阅读
凹凸曼00威威_694
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有