当前位置 主页 > 服务器问题 > win服务器问题汇总 >

    深入学习java内存化和函数式协同

    栏目:win服务器问题汇总 时间:2019-11-22 01:43

    前言

    所有编程语言都在增加函数特性,因为运行时已变得强大到足够适应性能或内存开销。函数式编程的许多收益之一是,您可将麻烦或容易出错的任务卸载到运行时。另一个收益是将函数特性简洁地组合到您代码中的能力。

    在本期文章中,我将探讨 Java 下一代语言中的内存化。然后,通过利用 Clojure 示例,我将展示通过利用函数特性之间的协调作用,如何实现常见问题的一般解决方案。

    内存化

    内存化 这个词是 Donald Michie(一位英国人工智能研究人员)发明的,用于表示重复的值的函数级缓存。如今,内存化在函数式编程语言中很常见,它要么被用作一个内置特性,要么被用作一个相对容易实现的特性。

    内存化在以下场景中很有帮助。假设您必须反复调用一个注重性能的函数。一个常见解决方案是构建内部缓存。每次计算某个参数集的值时,您都会将该值放入缓存中,作为参数值的线索。在未来,如果该函数使用以前的参数调用,那么它将会从缓存返回值,而不是重新计算它。函数缓存是一种经典的计算机科学权衡:它使用更多内存(我们常常拥有丰富的内存)来不断实现更高的性能。

    函数必须是纯粹的,缓存技术才能发挥其作用。纯函数 是没有副作用的函数:它没有引用任何其他易变的类字段,没有设置除返回值以外的任何值,而且仅依赖于参数作为输入。java.lang.Math 类中的所有方法都是纯函数的良好示例。显然,只有在函数可靠地为一组给定的参数返回相同值时,您才能成功地重用缓存的结果。

    Groovy 中的内存化

    内存化在 Groovy 中很简单,Groovy 在 Closure 类上包含一系列 memoize() 函数。例如,假设您有一个昂贵的哈希算法,以至于您需要缓存结果来提高效率。为此,您可以使用闭包块语法来定义方法,在返回时调用 memoize() 函数,如清单 1 所示。我并不是暗示清单 1 中使用的 ROT13 算法(即凯撒密码 的一个版本)的性能面临挑战,只是假设缓存在这个示例中很重要。

    清单 1. Groovy 中的内存化

    class NameHash {
    def static hash = {name ->
    name.collect{rot13(it)}.join()
    }.memoize()
    public static char rot13(s) {
    char c = s
    switch (c) {
    case 'A'..'M':
    case 'a'..'m': return c + 13
    case 'N'..'Z':
    case 'n'..'z': return c - 13
    default: return c
    }
    }
    }
    class NameHashTest extends GroovyTestCase {
    void testHash() {
    assertEquals("ubzre", NameHash.hash.call("homer")) }
    }

    正常情况下,Groovy 函数定义看起来像清单 1 中的 rot13(),方法主体位于参数列表之后。hash() 函数定义使用了稍微不同的语法,将代码块分配给 hash 变量。该定义的最后一部分是对 memoize() 的调用,它自动为重复的值创建一个内部缓存,与该参数建立联系。

    memoize() 方法实际上是一个方法系列,为您提供了对缓存特征的一定控制,如表 1 所示。

    表 1. Groovy 的 memoize() 系列

    方法 用途
    memoize() 返回闭包的一个包含缓存的实例
    memoizeAtMost() 为缓存元素的数量设置一个上限
    memoizeAtLeast(int protectedCacheSize) 为缓存元素的数量设置一个下限,保护一定数量的元素免遭垃圾收集
    memoizeBetween(int protectedCacheSize, int maxCacheSize) 为缓存元素的数量设置一个下限和上限