Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
绑定到记录实例的已记忆函数的Haskell生存期_Haskell_Memoization - Fatal编程技术网

绑定到记录实例的已记忆函数的Haskell生存期

绑定到记录实例的已记忆函数的Haskell生存期,haskell,memoization,Haskell,Memoization,首先,我是Haskell的初学者,所以要友善:) 考虑以下示例: {-#语言记录通配符#-} 数据项=项{itemPrice::Float,itemQuantity::Float}派生(显示,等式) 数据顺序=顺序{orderItems::[Item]}派生(Show,Eq) itemTotal::Item->Float itemTotal Item{..}=itemPrice*itemQuantity 订单总计::订单->浮动 orderTotal=总和。地图项目总数。订单项 是否可以记忆函

首先,我是Haskell的初学者,所以要友善:)

考虑以下示例:

{-#语言记录通配符#-}
数据项=项{itemPrice::Float,itemQuantity::Float}派生(显示,等式)
数据顺序=顺序{orderItems::[Item]}派生(Show,Eq)
itemTotal::Item->Float
itemTotal Item{..}=itemPrice*itemQuantity
订单总计::订单->浮动
orderTotal=总和。地图项目总数。订单项
是否可以记忆函数
orderTotal
,使其在
Order
记录的每个“实例”中只执行一次,并且,这是一个棘手的部分,一旦该订单被垃圾收集,绑定到此实例的缓存项将被删除?换句话说,我不想拥有一个永远增长的缓存

评论后编辑:

事实上,在这个简单的例子中,记忆的开销可能没有得到回报。但您可以想象这样一个场景:我们有一个复杂的值图(例如,订单、订单项、产品、客户等)和许多对这些值进行操作的派生属性(如上面的orderTotal)。如果我们为订单总数创建一个字段,而不是使用函数来计算它,那么我们必须非常小心,不要以不一致的订单结束

如果我们能够以声明的方式表达这些数据的相互依赖性(使用函数而不是字段),并将优化这些计算的任务委托给编译器,那岂不是一件好事?我相信,在像哈斯克尔这样的纯语言中,这是可能的,尽管我缺乏这样做的知识

为了说明我要说的内容,请看下面的代码(在Python中):

def记忆(功能):
函数名=函数名__
def包装(自我):
尝试:
结果=自身。\缓存[函数\名称]
除KeyError外:
结果=self.\u缓存[函数\u名称]=函数(self)
返回结果
返回属性(已包装)
类别项目:
定义初始(自身、价格、数量):
自身价格=价格
自身数量=数量
self.\u cache={}
@财产
def价格(自我):
返回自己的价格
@财产
def数量(自身):
返回自身数量
@回忆
def总计(自身):
返回自制价格*自制数量
Item
是不可变的(有点),因此我们知道每个派生属性在每个实例中只能计算一次。这正是
记忆功能所做的。此外,缓存位于实例本身内部(
self.\u cache
),因此它将被垃圾收集


我想要的是在Haskell中实现类似的功能。

对特定类型的值进行计算的一种相对简单的方法是将计算结果引入数据类型,并使用智能构造函数。也就是说,将
订单
数据类型写为:

data Order = Order
  { orderItems :: [Item]
  , orderTotal :: Float
  } deriving (Show, Eq)
请注意,
orderTotal
字段将替换同名函数。然后,使用智能构造函数构造订单:

order :: [Item] -> Order
order itms = Order itms (sum . map itemTotal $ itms)
由于延迟计算,仅在第一次需要时才计算
orderTotal
字段,并在此后缓存值。当
订单
被垃圾收集时,显然
订单总数
将同时被垃圾收集


有些人会将其打包到模块中,只导出智能构造函数
order
,而不是通常的构造函数
order
,以确保永远无法创建具有不一致
orderTotal
的订单。我担心这些人。他们如何度过他们的日常生活,知道他们可能会在任何时候出卖自己?无论如何,这对于真正的偏执狂来说是一个可行的选择。

我怀疑跟踪实时
订单
值的开销,以及它们的
订单总数
值的缓存,将大于重新计算值。在证明花费在
orderTotal
中的时间实际上是一个问题之前,不要担心记忆函数。即使假设记忆在这里有帮助,也无法通过仅修改
orderTotal
来实现所需。这充其量只能创建一个持续整个程序的缓存,或者为每个调用创建一个“缓存”(无用)。相反,您需要修改调用
orderTotal
的位置,以便在调用点创建缓存,多次使用,然后知道是时候扔掉它了。谢谢您的回复。我用更多的信息编辑了这个问题。不知道你的答案是否正确,但你对某些人的看法是提倡某些编程风格-他们绝对存在这更多的是关于推迟计算而不是记忆它。每个
订单
的总额都是按需计算的,但是如果两个
订单
有相同的项目,您仍然会计算两次总额。@kabuhr我非常喜欢您的解决方案,它既简单又优雅。我从来没有想到懒惰在这里会有所帮助。到目前为止,我看到的唯一缺点是,如果Order类型有很多属性,很难知道哪个参数是智能构造函数签名中的参数,因为它们没有命名。但我可以接受,我会接受你的回答。非常感谢你。