2017-07-07 100 views
1

在以下科特林例如,我想“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 < = 1.1不支持记忆。当然,您可以编写自己的记忆功能或使用库https://github.com/MarioAriasC/funKTionale

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

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? 
} 

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

+1

记忆是指缓存结果吗? – Joshua

+0

@Joshua,是的。感谢您的评论。我编辑了这个问题,并添加了“缓存结果”。 – Peti

回答

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函数在密钥中使用它们来查找答案,但这是无用的,因为它们永不改变(因为它们用val定义)。

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

+1

非常感谢@marstran,我学到了很多! – Peti

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()) 
     }) 
    } 
} 
+0

感谢@Joshua,给出的例子的优秀解决方案。我实际上过分简化了这个例子。在我的真实世界用例中,成员函数使用一个或多个参数来计算使用内部状态的内容。我必须调整这个问题或者用一个“tricker”例子来开启第二个问题...... – Peti

+1

@Peti我可以编辑答案,如果你给更多的细节=) – Joshua

+0

我改变了这个例子。现在成员函数有1个参数。 – Peti