调用SequenceType.forEach时是否有引用实例函数的方法? [英] Is there a way to reference instance function when calling SequenceType.forEach?

查看:69
本文介绍了调用SequenceType.forEach时是否有引用实例函数的方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑类型Foo:

class Foo {

    var isBaz: Bool {
        return false
    }

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

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

Now let's say I want to iterate through a collection of class instances and call some function on each of them:

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

someFoos.forEach { $0.bar() }

此语法非常紧凑,但感觉有点尴尬.而且,它不能在任何地方使用.例如,在if语句条件中:

This syntax is quite compact, but it feels a bit awkward. Also, it cannot be used everywhere. For example, in an if statement condition:

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
}

理想情况下,写类似这样的东西

Ideally, it would be nice to write something like

someFoos.forEach(Foo.bar)

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

but as of Swift 2.1 this is not a correct syntax. Such way of referencing the function would be similar to the following:

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

someFoos.forEach(bar2)

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

Is there a better way to reference instance function? How do you prefer to write such expressions?

推荐答案

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

There are two different problems here. The trailing closure syntax can be used when calling a function and the last parameter is a closure, so

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

完全等效.但是,在if语句的情况下,尾随闭包语法可能会出现问题:

are fully equivalent. However, the trailing closure syntax can be problematic in the condition of an if-statement:

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

We can only speculate why the second one does not work. It could be that the compiler takes the first { as the start of the if-body. Perhaps this will change in a future version of Swift but probably it is not worth the effort.

另一个问题是关于咖喱函数.

someFoos.forEach(bar2)

进行编译是因为bar2具有类型Foo -> Void,而这恰恰是 forEach()方法的预期结果.另一方面,Foo.bar 是咖喱函数(请参见 http://oleb.net /blog/2014/07/swift-instance-methods-curried-functions/),将实例作为第一个 争论.它的类型为Foo -> () -> ().所以

compiles because bar2 has the type Foo -> Void, and that is exactly what the forEach() method expects. Foo.bar, on the other hand, is a curried function (see http://oleb.net/blog/2014/07/swift-instance-methods-curried-functions/) which takes the instance as the first argument. It has the type Foo -> () -> (). So

Foo.bar(someFoo)

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

Foo.bar(someFoo)()

someFoo实例上调用bar方法.

(注意:以下内容并非实际建议, 但这只是为了说明咖喱功能和乐趣 闭包!)

(Note: The following is not meant as an actual recommendation, but only as a demonstration about curried functions and fun with closures!)

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

To pass Foo.bar directly as an argument to forEach() we need to "swap" the order of the parameters. Haskell has a "flip" function for that purpose, and it is also possible in Swift (see e.g. 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参数

Then flip(Foo.bar) has the type () -> Foo -> (), so the void argument of the bar method can be applied

flip(Foo.bar)()

获取Foo -> ()闭包,并且

flip(Foo.bar)()(someFoo)

调用someFoo实例上的bar方法. 现在我们可以打电话

calls the bar method on the someFoo instance. And now we can call

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

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

without using a closure expression { .. } !!

如果isBaz方法而不是属性

func isBaz() -> Bool { return false }

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

then you could do the same in the if-expression:

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

同样,这仅是示例.也是属性 不是咖喱函数,所以这不能用 您的isBaz属性.

Again, this is only meant as a demonstration. Also properties are not curried functions, so this cannot be done with your isBaz property.

这篇关于调用SequenceType.forEach时是否有引用实例函数的方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆