2017-07-07 126 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