2012-06-04 2 views
4

Я пытаюсь понять Активные шаблоны, так что я играю с FizzBuzz:FizzBuzz с активным Patterns

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 option?

ответ

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 
+0

+1 для полного активного шаблона (избили меня до него) –

3

Remove (ненужную) 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 } 
+0

Нет, он требует 'int option' из-за соответствия на' Some Fizz' и т. д. – Daniel

+0

@ Daniel: исправлено, все еще в режиме редактирования :) – pad

+2

Хороший улов при надевании FizzBuzz case первый. –

10

первое решение Даниила может быть упрощена, так как вы не» t действительно необходимо определить отдельный активный шаблон для 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" 
    | _ -> "" 
+0

+1: Я либо забыл, либо никогда не знал, что '&' был частью языка шаблонов! –

+0

@ Томас: Чтобы быть точным, это решение OP, исправлено. – Daniel