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

标签 ios swift

考虑 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)

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

最佳答案

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

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 的 future 版本中进行更改,但可能不值得 努力。


另一个问题是关于柯里化(Currying)函数

someFoos.forEach(bar2)

编译是因为 bar2 的类型是 Foo -> Void,这正是 forEach() 方法需要什么。 Foo.bar 另一方面, 是一个 curry 函数(参见 http://oleb.net/blog/2014/07/swift-instance-methods-curried-functions/ ),它将实例作为第一个 争论。它的类型为 Foo -> () -> ()。所以

Foo.bar(someFoo)

是类型为 () -> () 的闭包,并且

Foo.bar(someFoo)()

someFoo 实例上调用 bar 方法。

(注意:以下内容并非实际建议, 但仅作为有关柯里化(Currying)函数和乐趣的演示 有闭包!)

要将 Foo.bar 作为参数直接传递给 forEach() 我们需要 “交换”参数的顺序。为此,Haskell 有一个“翻转”功能, 在 Swift 中也是可能的(参见例如 How to write a flip method in Swift? ):

func flip<A, B, C>(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)

someFoo 实例上调用 bar 方法。 现在我们可以调用

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

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

如果 isBaz 是一个方法而不是一个属性

func isBaz() -> Bool { return false }

那你 可以在 if 表达式中做同样的事情:

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

再次声明,这只是一个演示。还有属性 不是 curry 函数,所以这不能用 你的 isBaz 属性。

关于ios - 有没有办法在调用 SequenceType.forEach 时引用实例函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34049116/

相关文章:

IOS/xcode : Searchbar for TableView with NSFETched Results Controller

c# - iOS 5 中的 NSJSONSerialization 就像 c# 序列化到类

ios - 如何在swift中重载赋值运算符

swift - 通过使用 swift 在 firebase 中搜索键来检索值

objective-c - NSSearchPathForDirectoriesInDomains 返回错误的目录

objective-c - 调整 UIButton 上的图像

ios - iOS "self.view.window?.addSubview()"和 "UIApplication.shared.keyWindow?.addSubview()"哪个最好

ios - String.anotherIndex 导致 "fatal error: cannot increment endIndex"

arrays - 如何在 swift 中初始化一组空数组?

swift - 弹出窗口演示从 xcode 7.3.1 中的手动/操作 segue 菜单中消失