使用"函数"语法执行模式匹配时如何检索值?

Sco*_*rod 1 f# pattern-matching

以下行不编译:

| IsNeither  -> sprintf "%i" // ???
Run Code Online (Sandbox Code Playgroud)

这是该行所属的功能:

let run = function

    | IsFizzBuzz -> "Fizz Buzz"
    | IsFizz     -> "Fizz"
    | IsBuzz     -> "Buzz"
    | IsNeither  -> sprintf "%i" // Doesn't compile
Run Code Online (Sandbox Code Playgroud)

这是整个程序: 模块Temp

let (|IsFizz|IsBuzz|IsFizzBuzz|IsNeither|) = function
    | n when n % 3 = 0 && 
             n % 5 = 0 -> IsFizzBuzz
    | n when n % 3 = 0 -> IsFizz
    | n when n % 5 = 0 -> IsBuzz
    | n ->                IsNeither

let run = function

    | IsFizzBuzz -> "Fizz Buzz"
    | IsFizz     -> "Fizz"
    | IsBuzz     -> "Buzz"
    | IsNeither  -> sprintf "%i" // Doesn't compile

let result = [1..16] |> List.map(run)
Run Code Online (Sandbox Code Playgroud)

我仍然可以使用签名上的"函数"语法提取值吗?

例:

let (|IsFizz|IsBuzz|IsFizzBuzz|IsNeither|) = function
Run Code Online (Sandbox Code Playgroud)

Ant*_*fer 9

通过使用as关键字,有一种解决方案不需要您重新定义模式:

let run = function
    | IsFizzBuzz -> "Fizz Buzz"
    | IsFizz     -> "Fizz"
    | IsBuzz     -> "Buzz"
    | IsNeither as n -> sprintf "%i" n 
Run Code Online (Sandbox Code Playgroud)

这很好地适用于活动模式,匹配有区别的联合案例等.一个突出的用例是类型检查,你有匹配的匹配 | :? string as s


The*_*ght 7

最简单的解决方案是使值成为模式的一部分.

let (|IsFizz|IsBuzz|IsFizzBuzz|IsNeither|) = function
    | n when n % 3 = 0 && 
             n % 5 = 0 -> IsFizzBuzz
    | n when n % 3 = 0 -> IsFizz
    | n when n % 5 = 0 -> IsBuzz
    | n                -> IsNeither n

let run = function
    | IsFizzBuzz  -> "Fizz Buzz"
    | IsFizz      -> "Fizz"
    | IsBuzz      -> "Buzz"
    | IsNeither n -> sprintf "%i" n
Run Code Online (Sandbox Code Playgroud)


Seh*_*cht 6

有一个neither案例表明并非所有值都可以划分为"域",因此部分活动模式可能是更好的选择.
AND模式相关联,它可以简化代码(以某些更改为代价)

// common code extracted and inlined (optional)
let inline isDivisibleBy divisor dividend =
  if dividend % divisor = LanguagePrimitives.GenericZero
  then Some ()
  else None

let (|Fizz|_|) = isDivisibleBy 3
let (|Buzz|_|) = isDivisibleBy 5

let run = function Fizz & Buzz -> "FizzBuzz"
                 | Fizz        -> "Fizz"
                 | Buzz        -> "Buzz"
                 | x           -> string x

let result = List.map run [1 .. 16]
Run Code Online (Sandbox Code Playgroud)

沿着这条道路走下去,人们甚至可以摆脱那些,FizzBuzz有利于一般将军IsDivisibleBy

let inline (|IsDivisibleBy|_|) divisor dividend =
  if dividend % divisor = LanguagePrimitives.GenericZero
  then Some ()
  else None

let run = function IsDivisibleBy 3 & IsDivisibleBy 5 -> "FizzBuzz"
                 | IsDivisibleBy 3                   -> "Fizz"
                 | IsDivisibleBy 5                   -> "Buzz"
                 | x                                 -> string x

let result = List.map run [1 .. 16]
Run Code Online (Sandbox Code Playgroud)

或者甚至那样,如果你仍然想要那些Fizz,Buzz你仍然可以做到

let (|Fizz|_|) = (|IsDivisibleBy|_|) 3
let (|Buzz|_|) = (|IsDivisibleBy|_|) 5
Run Code Online (Sandbox Code Playgroud)