Kotlin数据类成员函数的记忆

时间:2017-07-07 07:53:17

标签: functional-programming kotlin

在下面的Kotlin示例中,我想“memoize”(缓存结果)成员函数matches

import java.util.regex.Pattern

data class MyDataClass(val name: String = "John Doe",
                       val description: String = "Famous person") {

    //TODO memoize this
    fun matches(searchTerm: String): Boolean {
        println("Calculating...")
        return name.matches("(?i).*${Pattern.quote(searchTerm)}.*".toRegex())
                || description.matches("(?i).*${Pattern.quote(searchTerm)}.*".toRegex())
    }
}

fun main(args: Array<String>) {
    val myData = MyDataClass()
    println(myData.matches("John"))
    println(myData.matches("John"))
    println(myData.matches("John"))
    println(myData.matches("Famous"))
    println(myData.matches("Famous"))
    println(myData.matches("Famous"))    
}

据我所见,Kotlin&lt; = 1.1不支持memoization。当然,您可以编写自己的记忆功能或使用像https://github.com/MarioAriasC/funKTionale

使用funKTionale,我不必编写自己的memoization函数,而是来到这个解决方案。不幸的是它看起来像“样板”:

import org.funktionale.memoization.memoize
import java.util.regex.Pattern

data class MyMemoizedDataClassV1(val name: String = "John Doe",
                                 val description: String = "Famous person") {

    private val memoizedMatches: (String, String, String) -> Boolean =
            { name: String, description: String, searchTerm: String ->
                println("Calculating...")
                name.matches("(?i).*${Pattern.quote(searchTerm)}.*".toRegex())
                        || description.matches("(?i).*${Pattern.quote(searchTerm)}.*".toRegex())
            }.memoize()

    fun matches(searchTerm: String): Boolean {
        return memoizedMatches(name, description, searchTerm)
    }
}

fun main(args: Array<String>) {
    val myData = MyMemoizedDataClassV1()
    println(myData.matches("John"))
    println(myData.matches("John"))
    println(myData.matches("John"))
    println(myData.matches("Famous"))
    println(myData.matches("Famous"))
    println(myData.matches("Famous"))
}

我认为一个更好看的解决方案就像

一样
data class MyDataClass(val name: String = "John Doe",
                       val description: String = "Famous person") {

    fun matches(searchTerm: String): Boolean {
        println("Calculating...")
        return name.matches("(?i).*${Pattern.quote(searchTerm)}.*".toRegex())
                || description.matches("(?i).*${Pattern.quote(searchTerm)}.*".toRegex())
    }.memoize() //TODO how?
}

但是,如何实现这个目标?

2 个答案:

答案 0 :(得分:2)

你可以从你的Funktionale代码中删除很多样板:

data class MyMemoizedDataClassV1(val name: String = "John Doe",
                                 val description: String = "Famous person") {

    val matches = { searchTerm: String ->
        println("Calculating...")
        name.matches("(?i).*${Pattern.quote(searchTerm)}.*".toRegex())
           || description.matches("(?i).*${Pattern.quote(searchTerm)}.*".toRegex())
    }.memoize()

}

您可以从lambda中的数据类访问namedescription,因此您不必将它们作为参数传递。将它们作为参数将使memoize-function在密钥中使用它们来查找答案,但这是无用的,因为它们永远不会改变(因为它们是用val定义的)。

此外,由于matches的类型为(String) -> Boolean,因此您可以直接在数据类中公开函数属性,而不是创建另一个调用它的函数。最后,我删除了编译器可以推断的一些类型。

答案 1 :(得分:1)

abval,因此您可以将结果分配给val

data class MyDataClass(val a: Int = 1,
                       val b: Int = 2) {
    val sum = a + b
}

如果计算费用昂贵,可以使用延迟来延迟计算。

data class MyDataClass(val a: Int = 1,
                        val b: Int = 2) {
    val sum: Int by lazy {
        a + b
    }
}

编辑:已编辑问题的新答案

interface StringMatchable {
    fun matches(searchTerm: String): Boolean
}

data class MyDataClass(val name: String = "John Doe",
                       val description: String = "Famous person") : StringMatchable by 
CacheStringMatchable({
    searchTerm ->
    name.matches("(?i).*${Pattern.quote(searchTerm)}.*".toRegex())
            || description.matches("(?i).*${Pattern.quote(searchTerm)}.*".toRegex())
})

class CacheStringMatchable(private val function: (String) -> Boolean) : StringMatchable {
    private val map: MutableMap<String, Boolean> = mutableMapOf()
    override fun matches(searchTerm: String): Boolean {
        return map.computeIfAbsent(searchTerm, function)
    }
}

要委派方法,目前只能通过界面。因此,它可能无法以通用方式编写(即所有人都有1个缓存类)。

Edit2:如果这是唯一需要matches()的课程,那么这里有一个更简单的答案

data class MyDataClass(val name: String = "John Doe",
                       val description: String = "Famous person") {
    private val map: MutableMap<String, Boolean> = mutableMapOf()
    fun matches(searchTerm: String): Boolean {
        return map.computeIfAbsent(searchTerm, {
            searchTerm ->
            name.matches("(?i).*${Pattern.quote(searchTerm)}.*".toRegex())
                    || description.matches("(?i).*${Pattern.quote(searchTerm)}.*".toRegex())
        })
    }
}