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

Roo*_*oxo 4 haskell types functional-programming

给出以下 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
 } }
Run Code Online (Sandbox Code Playgroud)

有两个字段aandb和 ,在构造对象时,我通常只能访问其中一个字段,但另一个字段可以根据另一个字段进行计算。一个简单的例子是多边形,它要么被定义为点列表的凸包,要么被定义为线列表的交集(以及多面体的高维模拟)。b计算起来可能会非常昂贵a,因此我不想在创建对象时立即进行计算,而是等到实际需要时才进行计算。

我对此的一个想法是拥有一种记录类型

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

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


makePB :: B -> P 
-- ...
Run Code Online (Sandbox Code Playgroud)

其中两者从ormakeP创建一个。然后,每当我实际需要其中一个字段时,我都可以创建一个函数,类似于上面的函数,如果需要,它会计算该字段,然后返回两个字段都不再存在的新记录。但这似乎过于笨重,因为创建 a 需要一些计算(例如计算点的凸包),因此不能像通常那样创建记录。此外,我必须通过调用函数来封装其他函数中的每次使用,以确保实际计算值,然后访问该字段,我仍然必须确保是,而不是因为类型是。PABgetNothingPPgetJust ANothingMaybe A

有更好的方法来解决这个问题吗?

lef*_*out 10

这容易多了。只需包含theAthe B,两者都不是可选的,并依赖于惰性 \xe2\x80\x93 它负责所有决定是否需要自动计算其中一个值。

\n
data P = {a::A, b::B} \n\ncalculateBfromA :: A -> B\ncalculateAfromB :: B -> A\n\nmakePA :: A -> P\nmakePA a' = P a' (calculateBfromA a')\n\nmakePB :: B -> P\nmakePB b' = P (calculateAfromB b') b'\n
Run Code Online (Sandbox Code Playgroud)\n

您可能会说,Haskell 实际上null也像 Java 一样具有值,但它们始终与计算正确类型的正确值的方法相关联。Maybe是为了null实际上具有指称含义的值,但在您的示例中它们仅具有操作含义,Haskell 可以将其抽象掉。

\n

  • 哇,懒惰的评价一直让我印象深刻。我不认为这会那么容易工作。谢谢! (2认同)