haskell - 绑定到记录实例的记忆函数的 Haskell 生命周期
问题描述
首先,我是 Haskell 的初学者,所以请善待 :)
考虑以下示例:
{-# LANGUAGE RecordWildCards #-}
data Item = Item {itemPrice :: Float, itemQuantity :: Float} deriving (Show, Eq)
data Order = Order {orderItems :: [Item]} deriving (Show, Eq)
itemTotal :: Item -> Float
itemTotal Item{..} = itemPrice * itemQuantity
orderTotal :: Order -> Float
orderTotal = sum . map itemTotal . orderItems
是否可以记住该函数orderTotal
,使其每个Order
记录的“实例”只执行一次,这是棘手的部分,一旦这个订单被垃圾收集,绑定到这个实例的缓存条目就会被消除?换句话说,我不想拥有一个永远增长的缓存。
评论后编辑:
事实上,在这个简单的例子中,记忆的开销可能没有得到回报。但是你可以想象这样一个场景,我们有一个复杂的值图(例如订单、订单项、产品、客户......)和许多对这些值进行操作的派生属性(如上面的 orderTotal)。如果我们为订单总额创建一个字段,而不是使用函数来计算它,我们必须非常小心,以免导致订单不一致。
如果我们能够以声明的方式表达这些数据的相互依赖关系(使用函数而不是字段)并将优化这些计算的工作委托给编译器,那不是很好吗?我相信在像 Haskell 这样的纯语言中这是可能的,尽管我缺乏这样做的知识。
为了说明我想说的话,看看这段代码(在 Python 中):
def memoized(function):
function_name = function.__name__
def wrapped(self):
try:
result = self._cache[function_name]
except KeyError:
result = self._cache[function_name] = function(self)
return result
return property(wrapped)
class Item:
def __init__(self, price, quantity):
self._price = price
self._quantity = quantity
self._cache = {}
@property
def price(self):
return self._price
@property
def quantity(self):
return self._quantity
@memoized
def total(self):
return self.price * self.quantity
该类Item
是不可变的(有点),所以我们知道每个派生属性每个实例只能计算一次。这正是该memoized
函数的作用。除此之外,缓存存在于实例本身 ( self._cache
) 中,因此它会被垃圾收集。
我正在寻找的是在 Haskell 中实现类似的事情。
解决方案
记忆对特定类型值的计算的一种相对简单的方法是将计算结果带入数据类型并使用智能构造函数。也就是说,将Order
数据类型写为:
data Order = Order
{ orderItems :: [Item]
, orderTotal :: Float
} deriving (Show, Eq)
请注意,该orderTotal
字段将替换您的同名函数。然后,使用智能构造函数构造订单:
order :: [Item] -> Order
order itms = Order itms (sum . map itemTotal $ itms)
由于延迟评估,该orderTotal
字段将仅在第一次需要时计算,然后缓存值。当Order
被垃圾收集时,显然orderTotal
将同时被垃圾收集。
有些人会把它打包到一个模块中,只导出智能构造函数order
而不是通常的构造函数Order
,以确保orderTotal
永远不会创建不一致的订单。我很担心这些人。知道自己随时可能出卖自己,他们是如何度过日常生活的?无论如何,对于真正的偏执狂来说,这是一个可用的选择。
推荐阅读
- django - 跨越多行的django标签
- erlang - 如何在erlang中匹配子字符串忽略大小写
- java - 如何避免文件操作中的并发操作(保存/删除)
- c# - 属性集,在其他类中为空。C#
- django - Django 模型 Slug 字段 unique_for_month 不起作用
- python - 将 Python 请求与 Siteminder 一起使用
- algorithm - 如何生成仅具有路径压缩的不相交集的最坏情况?
- javascript - 在下面的链接中,他们为什么在 javascript 步骤中的 slides[slideIndex-1].style.display = "block" 处添加“-1”?
- java-8 - 如何通过自定义转换器和管道使用 Spring Data Redis for Hash
- javascript - 比较数字时如何解决问题?