4

我试图理解活动模式,所以我在玩 FizzBu​​zz:

let (|Fizz|_|) i = if i % 3 = 0 then Some Fizz else None
let (|Buzz|_|) i = if i % 5 = 0 then Some Buzz else None
let (|FizzBuzz|_|) i = if i % 5 = 0 && i % 3 = 0 then Some FizzBuzz else None

let findMatch = function
    | Some Fizz -> "Fizz"
    | Some Buzz -> "Buzz"
    | Some FizzBuzz -> "FizzBuzz"
    | _ -> ""

let fizzBuzz = seq {for i in 0 .. 100 -> Some i}
                |> Seq.map (fun i -> i, findMatch i)

这基本上是正确的方法,还是有更好的方法在这里使用活动模式?我不应该能够findMatch使用 int 而不是 int 选项吗?

4

3 回答 3

10

Daniel 的第一个解决方案可以简化,因为您实际上不需要为FizzBuzz. 这种情况可以描述为既FizzBuzz匹配,也可以用模式语言很好地表达:

let findMatch = function 
  | Fizz & Buzz -> "FizzBuzz" 
  | Fizz -> "Fizz" 
  | Buzz -> "Buzz" 
  | _ -> "" 

let fizzBuzz = [ for i in 0 .. 100 -> findMatch i ]

Fizz & Buzz如果两者都匹配Fizz,则模式Buzz匹配。这依赖于模式首先匹配的事实,因此在这种情况下顺序是相关的。我还将您的最后一行缩短为我喜欢的样式并且更短(但意见不同)。

或者,如果您不想定义太多单一用途的活动模式,您还可以编写一个参数化的活动模式来测试输入是否可以被任何指定的数字整除:

let (|DivisibleBy|_|) by n = if n%by=0 then Some DivisibleBy else None

let findMatch = function 
  | DivisibleBy 3 & DivisibleBy 5 -> "FizzBuzz" 
  | DivisibleBy 3 -> "Fizz" 
  | DivisibleBy 5 -> "Buzz" 
  | _ -> "" 
于 2012-06-05T04:27:01.950 回答
8

你的findMatch功能应该是:

let findMatch = function
    | FizzBuzz -> "FizzBuzz" (* should be first, as pad pointed out *)
    | Fizz -> "Fizz"
    | Buzz -> "Buzz"
    | _ -> ""

您可以重写最后几行:

let fizzBuzz = Seq.init 100 (fun i -> i, findMatch i)

您的活动模式很好。一种替代方法是使用完整的活动模式:

let (|Fizz|Buzz|FizzBuzz|Num|) i = 
    match i % 3, i % 5 with
    | 0, 0 -> FizzBuzz
    | 0, _ -> Fizz
    | _, 0 -> Buzz
    | _ -> Num i
于 2012-06-04T20:08:41.427 回答
3

删除(不必要的)Some,以便findMatch函数int作为其参数:

let findMatch = function
    | FizzBuzz -> "FizzBuzz" (* Should be the first pattern *)
    | Fizz -> "Fizz"
    | Buzz -> "Buzz"
    | _ -> ""

let fizzBuzz = seq { for i in 0..100 -> i, findMatch i }
于 2012-06-04T20:15:15.637 回答