在F#模式匹配中拆分代码块以提高可读性

时间:2017-07-23 11:08:56

标签: f# pattern-matching lazy-evaluation code-readability active-pattern

<div class="col-md-10">
     <input asp-for="Address" class="form-control" />
     <span asp-validation-for="Address" class="text-danger"></span>
</div>

在使用“匹配”的模式匹配中,每个模式的代码可能很大,请参阅上面的 Foo ,这使我想要将这些块拆分为单独的调用以提高可读性。

这个问题可能是即使模式不匹配也会评估调用,如上面的 Bar

  • 选项1:懒惰评估。
  • 选项2:转发参数/参数。
  • 选项3:转发参数/参数并使用活动模式。

在每个模式下的代码可能很大的情况下,提高可读性的首选方法是什么。或者还有其他明显的解决方案吗?

// Standard pattern matching.
let Foo x =
  match x with
  | 1 ->
      // ... lots of code, only evaluated if x == 1
  | 2 ->
      // ... lots of code, only evaluated if x == 2

// Standard pattern matching separated out, causing exception.
let Bar x =
  let valueOne = //... lots of code, evaluated always. Exception if value <> 1.
  let valueTwo = //... lots of code, evaluated always. Exception if value <> 2.

  match x with
  | 1 -> valueOne
  | 2 -> valueTwo

2 个答案:

答案 0 :(得分:6)

我可能只是将模式匹配的两个主体提取到带unit的函数中:

let caseOne () = 
  // Lots of code when x=1

let caseTwo () = 
  // Lots of code when x=2

let Foo x =
  match x with
  | 1 -> caseOne()
  | 2 -> caseTwo()

这类似于使用lazy的解决方案,但是因为我们永远不会重复使用延迟值的结果,所以实际上没有理由使用延迟值 - 函数更简单并且它也延迟了对身体的评价。

如果您发现caseOnecaseTwo之间存在某些共性,您可以再次将其提取到另一个可以调用的函数中。

答案 1 :(得分:4)

一般来说,我试着让我的代码的语义与我试图完成的逻辑相匹配。在您的情况下,我会将您的问题描述为:

  

我可能收到两种不同的简单数据。基于哪个   我收到的,运行一段特定的代码。

这完全映射到选项2.根据上下文,我可能会也可能不会像您一样嵌套函数。其他两个选项会导致您的目标与代码不匹配。

选项1:

我会将这个逻辑描述为:

  

我现在有信息(或上下文),我可以从中构建两个不同的计算,其中一个可能需要稍后运行。现在构造它们,然后评估稍后需要的那个。

由于上下文或可用数据没有变化,这实际上与您想要的逻辑不匹配。你仍然处于相同的上下文中,因此使其变得懒惰的额外复杂性简单地模糊了函数的逻辑。

选项3:

我会将这个逻辑描述为:

  

我有一些数据可能适合两种情况之一。确定哪种情况需要一些复杂的逻辑,并且该逻辑也可能与确定整个函数所需返回值所需的逻辑有一些重叠。移动逻辑以将案例确定为单独的函数(在这种情况下为活动模式),然后使用活动模式的返回值来确定函数的结果值。

这个将控制流(确定我接下来应该执行哪些代码)与每种情况下执行的逻辑混淆。如果两者之间没有重叠,则将控制流与以下逻辑分开会更加明确。

因此,将代码的结构与问题的结构相匹配,或者最终会让人们想知道额外的代码复杂性会带来什么。