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

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

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

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

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

// ===== OPTION 1 =====
// Pattern matching separated out, lazy eval.
let Foo x =
  let valueOne = lazy //... lots of code, evaluated on Force().
  let valueTwo = lazy //... lots of code, evaluated on Force().

  match x with
  | 1 -> valueOne.Force()
  | 2 -> valueTwo.Force()

// ===== OPTION 2 =====
// Pattern matching separated out, with arguments.
let Foo x =
  let valueOne a = //... lots of code.
  let valueTwo a = //... lots of code.

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

// ===== OPTION 3 =====
// Active Pattern matching separated out, with arguments.
let Foo x = 
  let (|ValueOne|_|) inp =
    if inp = 1 then Some(...) else None

  let (|ValueTwo|_|) inp =
    if inp = 2 then Some(...) else None

  match x with
  | ValueOne a -> a
  | ValueTwo b -> b
4

2 回答 2

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,但由于我们从不重复使用惰性值的结果,因此实际上没有理由使用惰性值 - 函数更简单,它也会延迟对主体的评估。

如果您随后发现caseOne和之间有一些共性caseTwo,您可以再次将其提取到另一个他们都可以调用的函数中。

于 2017-07-23T13:33:46.940 回答
4

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

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

这完全映射到选项 2。根据上下文,我可能会或可能不会像您一样嵌套函数。其他两个选项会在您的目标和代码之间造成不匹配。

选项1:

我将这个逻辑描述为:

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

这确实不符合您想要的逻辑,因为上下文或可用数据没有变化。无论哪种方式,您仍然处于相同的上下文中,因此使其变得惰性的额外复杂性只会混淆函数的逻辑。

选项 3:

我将这个逻辑描述为:

我有一些数据可能适合两种情况之一。确定哪种情况成立需要一些复杂的逻辑,并且该逻辑也可能与确定整个函数所需的返回值所需的逻辑有一些重叠。将判断 case 的逻辑移到单独的函数中(本例中为 active 模式),然后使用 active 模式的返回值来确定函数的结果值。

这将控制流(确定我接下来应该执行什么代码)与每种情况下执行的逻辑混为一谈。如果两者之间没有重叠,则将控制流与以下逻辑分开会更清楚。

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

于 2017-07-23T13:20:59.653 回答