Aus*_*ett 4 haskell functional-programming lazy-evaluation
在具有可变状态的命令式/面向对象编程中,声明如下结构将是非常常见和有用的:
struct RigidBody {
float m_mass;
float m_inverseMass;
Mat3 m_localInverseInertiaTensor;
Mat3 m_globalInverseInertiaTensor;
Vec3 m_globalCentroid;
Vec3 m_localCentroid;
Vec3 m_position;
Mat3 m_orientation;
Vec3 m_linearVelocity;
Vec3 m_angularVelocity;
};
Run Code Online (Sandbox Code Playgroud)
资料来源:http://allenchou.net/2013/12/game-physics-motion-dynamics-implementations/
这里有许多属性可以直接从其他属性计算,例如m_inverseMassfrom m_mass.在像Haskell这样的无状态编程语言中,获取派生值很容易:
data RigidBody = RigidBody {mass :: Float}
inverseMass :: RigidBody -> Float
inverseMass body = 1 / mass body
Run Code Online (Sandbox Code Playgroud)
但这会计算inverseMass每次我们需要的时间,特别是在性能至关重要的领域,如物理模拟,这会变得很昂贵.我考虑过memoization,但我不确定这是否是表达这种依赖属性的懒惰评估的好方法,因为它似乎是一个复杂的解决方案.如何存储衍生价值而无需重新计算它们?
K. *_*uhr 11
正如@ 4castle和@Shersh所说,一个简单的方法是将派生值包含在数据类型中:
data RigidBody = RigidBody
{ m_mass :: Float
, m_inverseMass :: Float }
Run Code Online (Sandbox Code Playgroud)
然后使用智能构造函数创建新的RigidBodys:
rigidBody mass = RigidBody mass (1/mass)
Run Code Online (Sandbox Code Playgroud)
该表达式1/mass将创建一个thunk m_inverseMass,在第一次评估之后,它将在不重新计算的情况下可用,因此它提供了一种自动记忆.
更一般的转换,例如改变位置和global*基于local*值正确地更新所有字段,将以类似的方式处理.作为简化示例:
module Rigid where
type Vec3 = Double -- just to type check
data RigidBody = RigidBody
{ m_mass :: Float
, m_inverseMass :: Float
, m_pos :: Vec3
, m_localCentroid :: Vec3
, m_globalCentroid :: Vec3
}
rigidBody mass pos centroid =
RigidBody mass (1/mass) pos centroid (centroid + pos)
move body delta =
rigidBody (m_mass body)
(m_pos body + delta)
(m_localCentroid body)
Run Code Online (Sandbox Code Playgroud)
在性能至关重要的应用程序中,您可能希望采取措施在适当的位置引入严格性,这样您就不会积累大量未评估的thunk.
| 归档时间: |
|
| 查看次数: |
113 次 |
| 最近记录: |