在函数语言中编写纯函数时,如何避免不必要的计算



我有两个函数,它们是纯函数的组合。第一个功能是拿一个包裹,在上面盖一栋房子,然后拍张照片在杂志上做广告:

let buildAndAdvertiseHouse parcel = 
    parcel
    |> inspect
    |> buildWalls
    |> buildRoof
    |> takePhoto
    |> advertise

第二个功能还拿走了一个包裹,在上面盖了一栋房子,并为它添加了点睛之笔:

let buildAndCompleteHouse parcel = 
    parcel
    |> inspect
    |> buildWalls
    |> buildRoof
    |> paintWalls
    |> addFurniture

很明显,这两个函数也是纯的,因为它们是纯函数的组合。现在我有一个包,比方说niceParcel,我想把这两个函数都应用到它上。但是,我想避免前三个子函数被计算两次,因为它们需要很长时间来计算,而且它们在两个函数之间是共享的。

我该如何重构我的代码,从而避免这些不必要的计算,同时保留这些有明确含义的漂亮的纯函数?

正如评论中提到的,我认为最好的方法是将公共部分转换为build函数。即使您不打算将函数用于其他目的,这也是构建函数代码的一种干净方法。

在F#中,您可以定义一个类型来表示部分建成的房屋,但不公开其内部。这意味着你的图书馆的呼叫者可以使用build来建造一个部分建造的房子,但他们唯一能做的就是使用你提供的两个功能:

module Houses = 
  type House = private HouseData of <whatever>
  let build parcel = (...)
  let buildAndAdvertiseHouse house = 
    house
    |> takePhoto
    |> advertise
  let buildAndCompleteHouse house = 
    house
    |> paintWalls
    |> addFurniture

你可以隐藏这样一个事实,即在你可以做广告之前,一个人需要建造一所房子;以各种方式完成它。例如,如果你通常同时执行这两个操作,那么你可以定义一个调用所有三个函数的函数,而你的库的用户可以直接使用它,也可以学习更多关于房屋建筑的知识,如果他们需要更精细的控制,可以使用这三个函数。

另一种方法是将功能封装在一个简单的类型中。F#混合了函数式和面向对象的风格,所以拥有一个只运行一次公共部分并保持一些状态的类型并没有什么错。

type House(parcel) = 
  let house = 
    parcel
    |> inspect
    |> buildWalls
    |> buildRoof
  member x.BuildAndAdvertiseHouse()
    house
    |> takePhoto
    |> advertise
  member x.BuildAndCompleteHouse() = 
    house
    |> paintWalls
    |> addFurniture

这在F#中很好,但我想我更喜欢带有build函数的函数方法。

最新更新