FizzBuzz с активными паттернами

Я пытаюсь понять Active Patterns, поэтому играю с FizzBuzz:

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?


person Nick Heiner    schedule 04.06.2012    source источник


Ответы (3)


Ваша 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
person Daniel    schedule 04.06.2012

Первое решение Дэниела можно упростить, потому что вам фактически не нужно определять отдельный активный шаблон для FizzBuzz. Случай может быть описан как совпадение как Fizz, так и Buzz, что может быть красиво выражено на языке шаблонов:

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" 
  | _ -> "" 
person Tomas Petricek    schedule 05.06.2012
comment
+1: Я либо забыл, либо никогда не знал, что & был частью языка шаблонов! - person Stephen Swensen; 05.06.2012

Удалите (ненужный) 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 }
person pad    schedule 04.06.2012
comment
Нет, требуется int option из-за соответствия Some Fizz и т. Д. - person Daniel; 05.06.2012
comment
Хорошая уловка, поставив на первое место футляр FizzBuzz. - person Onorio Catenacci; 05.06.2012