如何在 Kotlin 中组合 Lambdas

huangapple go评论76阅读模式
英文:

How to combine lambdas in Kotlin

问题

我目前正在尝试比较Java的lambda与Kotlin的lambda。我认为Kotlin的一个优势是不需要使用函数式接口来创建lambda表达式。在Java中,要想很好地使用lambda表达式和集合操作,需要对所有函数式接口有一个相对清晰的概览。

但是在我的研究中,我也找到了一个负面的观点。我认为Java中的Predicate有一个默认函数,它可以让你组合一些lambda表达式。

所以在Java中,你可以像这样做:

final Predicate<Person> isAdult = person -> person.getAge() >= 18;
final Predicate<Person> isMale = person -> person.getGender() == Gender.MALE;

// 组合
final Predicate<Person> isAdultAndMale = isAdult.and(isMale);

我认为在Kotlin中没有相等的功能。我在《Head First Kotlin》中读到有一个combine函数。但在Kotlin Playground中它不起作用。所以我最终使用了一个扩展函数,分别为两个lambda和多于两个lambda写了两个扩展函数,后者使用了varargs。

enum class Gender { MALE, FEMALE }
enum class EyeColor { BLUE, GREEN }

data class Person(
    var name: String,
    var age: Int,
    var gender: Gender,
    var eyeColor: EyeColor
)

fun List<Person>.combineTwoLambdas(firstLambda: (Person) -> Boolean, secondLambda: (Person) -> Boolean): List<Person> {
    return this.filter(firstLambda).filter(secondLambda)
}

fun List<Person>.combineMoreLambdas(vararg personFilters: (Person) -> Boolean): List<Person> {
    var myPersons = listOf<Person>()

    personFilters.forEach {
        myPersons = this.filter(it)
    }

    return myPersons
}

typealias PredicatePersons = (Person) -> Boolean

fun main() {
    val persons = listOf(
        Person("Susi", 20, Gender.FEMALE, EyeColor.BLUE),
        Person("Ralf", 19, Gender.MALE, EyeColor.BLUE),
        Person("Michael", 20, Gender.MALE, EyeColor.GREEN)
    )

    val isAdult: (Person) -> Boolean = { person -> person.age >= 18 }
    val isMale: (Person) -> Boolean = { it.gender == Gender.MALE }
    val hasGreenEyes: PredicatePersons = { it.eyeColor == EyeColor.GREEN }

    // 组合两个lambda
    val isAdultAndMale = persons.combineTwoLambdas(isAdult, isMale)

    // 组合多于两个lambda
    val isAdultAndMaleAndHasGreenEyes = persons.combineMoreLambdas(isAdult, isAdult, hasGreenEyes)

    print("combine all $isAdultAndMaleAndHasGreenEyes")
}

是否有一种更简单地链接多个lambda的方法?谢谢。

更新

这里是更新,感谢@Sweeper的建议:

val isAdultAndMale: (Person) -> Boolean = { isAdult(it) && isMale(it) }

// 使用别名
val isAdultAndMale: PredicatePersons = { isAdult(it) && isMale(it) }
英文:

I am currently trying to compare Java lambdas with Kotlin lambdas. I think it is a advantage that kotlin doesnt need Functional Interfaces for creating a lambda. I think it is a little bit different to get an overview of all Funtional Interfaces in Java to use lambdas and collection manipulation very well.

But I found a negative point also during my research. I think a Predicate in Java has a default function and which lets you combine some lambdas.

So you can do something like this in Java:

final Predicate&lt;Person&gt; isAdult = person -&gt; person.getAge() &gt;= 18;
final Predicate&lt;Person&gt; isMale = person -&gt; person.getGender() == Gender.MALE;

// Combine 
final Predicate&lt;Person&gt; isAdultAndMale = isAdult.and(isMale);

I think there is now equivalent in Kotlin. I read in Head first Kotlin that there is a combine function. But it doesn't work in the Kotlin playground.
So I end up with an extension function. Respectively with two extension functions. One for two lambdas and another for more than two lambdas and with varargs.

enum class Gender{MALE, FEMALE}
enum class EyeColor{BLUE, GREEN}

data class Person(
    var name: String, 
    var age: Int, 
    var gender: Gender,
	var eyeColor: EyeColor
)

fun List&lt;Person&gt;.combineTwoLambdas(firstLambda: (Person) -&gt; Boolean, secondLambda: (Person) -&gt; Boolean): List&lt;Person&gt; {
    return this.filter(firstLambda).filter(secondLambda)
}

fun List&lt;Person&gt;.combineMoreLambdas(vararg personFilters: (Person) -&gt; Boolean): List&lt;Person&gt; {
    var myPersons = listOf&lt;Person&gt;()
    
    personFilters.forEach {
        myPersons = this.filter(it)
    }
    
    return myPersons
}

typealias PredicatePersons = (Person) -&gt; Boolean

fun main() {
    val persons = listOf(
        Person(&quot;Susi&quot;, 20, Gender.FEMALE, EyeColor.BLUE), 
        Person(&quot;Ralf&quot;, 19, Gender.MALE, EyeColor.BLUE),
    	Person(&quot;Michael&quot;, 20, Gender.MALE, EyeColor.GREEN))
    
    val isAdult: (Person) -&gt; Boolean = {person -&gt; person.age &gt;= 18}
    val isMale: (Person) -&gt; Boolean = {it.gender == Gender.MALE}
    val hasGreenEyes: PredicatePersons = {it.eyeColor == EyeColor.GREEN}
    
    // combine two lambdas
    val isAdultAndMale = persons.combineTwoLambdas(isAdult, isMale)
    
    // combine more than two lambdas
    val isAdultAndMaleAndHasGreenEyes = persons.combineMoreLambdas(isAdult, isAdult, hasGreenEyes)
    
 
    print(&quot;combine all ${isAdultAndMaleAndHasGreenEyes}&quot;)
}

Is it somehow easier to chain multiple lambdas? Thanks.

UPDATE

Here is an update. Thanks to @Sweeper

val isAdultAndMale: (Person) -&gt; Boolean = {isAdult(it) &amp;&amp; isMale(it)}

// with alias
val isAdultAndMale: PredicatePersons = {isAdult(it) &amp;&amp; isMale(it)}

答案1

得分: 3

你可以轻松地定义 andor 的等价扩展函数:

fun <T> ((T) -> Boolean).and(arg: (T) -> Boolean): (T) -> Boolean = { this(it) && arg(it) }
fun <T> ((T) -> Boolean).or(arg: (T) -> Boolean): (T) -> Boolean = { this(it) || arg(it) }

...
val isAdultAndMale = isAdult.and(isMale)
英文:

You can define equivalents of and and or easily as extension functions:

fun &lt;T&gt; ((T) -&gt; Boolean).and(arg: (T) -&gt; Boolean): (T) -&gt; Boolean = { this(it) &amp;&amp; arg(it) }
fun &lt;T&gt; ((T) -&gt; Boolean).or(arg: (T) -&gt; Boolean): (T) -&gt; Boolean = { this(it) || arg(it) }

...
val isAdultAndMale = isAdult.and(isMale)

huangapple
  • 本文由 发表于 2020年4月9日 13:51:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/61114760.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定