简体   繁体   English

函数式编程中具有缓存计算的数据类型

[英]Data Type with cached calculations in functional programming

Given the following example code in OOP style, what is a good way to get this behavior in a purely functional style, eg in Haskell鉴于以下 OOP 风格的示例代码,以纯功能风格获得此行为的好方法是什么,例如在 Haskell

class P{
  private A a;
  private B b;
 
  public P(A a'){
     this.a=a';
     this.b=null;
  }
 
 public P(B b'){
     this.a = null;
     this.b = b';
 }

 public A getA(){ 
    if(this.a==null){
       //code to calculate a from b
       this.a = result;
    }
   return this.a
 } 

 public B getB(){ 
    if(this.b==null){
       //code to calculate b from a
       this.b = result;
    }
   return this.b
 } }

There are two fields a and b and when constructing the object, I often only have access to either one, but the other one can be calculated from the other.有两个字段ab ,在构建 object 时,我通常只能访问其中一个,但另一个可以从另一个计算。 An easy example for this would be polygons that are either defined as the convex hull of a list of points or as the intersection of a list of lines (and the higher-dimensional analog for polyhedrons).一个简单的例子是多边形,它要么被定义为点列表的凸包,要么被定义为线列表的交点(以及多面体的更高维模拟)。 It can be quite expensive to calculate b from a and vice-versa, so I don't want to immediately do the calculation when creating the object and instead wait until it is actually needed.a计算b可能会非常昂贵,反之亦然,因此我不想在创建 object 时立即进行计算,而是等到实际需要时再进行计算。

One idea I had for this would be to have a record type我对此的一个想法是有一个记录类型

data P = {a:: Maybe A, b:: Maybe B} 

makePA :: A -> P 
-- ...


makePB :: B -> P 
-- ...

where the two makeP create a P from either A or B .其中两个makePAB创建一个P Then whenever I actually need one of the fields I could make a get function, similar to the above that calculates the field if needed and then returns the new record with both fields no longer Nothing .然后,每当我真正需要其中一个字段时,我可以get function,类似于上面计算字段(如果需要),然后返回两个字段不再为Nothing的新记录。 But this seems overly clunky, as there's some calculation needed for creating a P (eg calculating the convex hull of points) and so the record can't just be created like one would usually do.但这似乎过于笨拙,因为创建P需要一些计算(例如计算点的凸包),因此不能像通常那样创建记录。 Furthermore, I'd have to encapsulate each use of P in other functions by a call to the get functions just to make sure the value is actually calculated and then access the field, which I still have to make sure is Just A and not Nothing due to the type being Maybe A .此外,我必须通过调用get函数来封装P在其他函数中的每次使用,以确保实际计算出该值,然后访问该字段,我仍然必须确保它是Just A而不是Nothing由于类型是Maybe A

Is there a better way to solve this?有没有更好的方法来解决这个问题?

It's much easier.这要容易得多。 Simply include both the A and the B , neither of them optionally, and rely on lazyness – it takes care of all deciding-whether-one-of-the-values-needs-to-be-calculated automatically.只需同时包含AB ,它们都不是可选的,并且依赖于惰性——它会自动处理所有决定是否需要计算的值。

data P = {a::A, b::B} 

calculateBfromA :: A -> B
calculateAfromB :: B -> A

makePA :: A -> P
makePA a' = P a' (calculateBfromA a')

makePB :: B -> P
makePB b' = P (calculateAfromB b') b'

You could say, Haskell actually has null values too like Java does, but they are always associated with a method to compute a proper value of the correct type.你可以说,Haskell 实际上也有null值,就像 Java 一样,但它们总是与计算正确类型的正确值的方法相关联。 Maybe is for null values that actually have a denotional meaning, but in your example they only have an operational meaning, which Haskell can abstract away. Maybe是针对实际上具有概念意义的null值,但在您的示例中,它们仅具有操作意义, Haskell 可以抽象掉。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM