Cómo codificar Fizzbuzz en F#
(10)
Creo que ya tienes la "mejor" solución.
Si desea mostrar más funcional / F # -ismos, podría hacer, por ejemplo
[1..100]
|> Seq.map (function
| x when x%5=0 && x%3=0 -> "FizzBuzz"
| x when x%3=0 -> "Fizz"
| x when x%5=0 -> "Buzz"
| x -> string x)
|> Seq.iter (printfn "%s")
y use listas, secuencias, mapas, iter, patrones y aplicaciones parciales.
[1..100] // I am the list of numbers 1-100.
// F# has immutable singly-linked lists.
// List literals use square brackets.
|> // I am the pipeline operator.
// "x |> f" is just another way to write "f x".
// It is a common idiom to "pipe" data through
// a bunch of transformative functions.
Seq.map // "Seq" means "sequence", in F# such sequences
// are just another name for IEnumerable<T>.
// "map" is a function in the "Seq" module that
// applies a function to every element of a
// sequence, returning a new sequence of results.
(function // The function keyword is one way to
// write a lambda, it means the same
// thing as "fun z -> match z with".
// "fun" starts a lambda.
// "match expr with" starts a pattern
// match, that then has |cases.
| x when x%5=0 && x%3=0
// I''m a pattern. The pattern is "x", which is
// just an identifier pattern that matches any
// value and binds the name (x) to that value.
// The "when" clause is a guard - the pattern
// will only match if the guard predicate is true.
-> "FizzBuzz"
// After each pattern is "-> expr" which is
// the thing evaluated if the pattern matches.
// If this pattern matches, we return that
// string literal "FizzBuzz".
| x when x%3=0 -> "Fizz"
// Patterns are evaluated in order, just like
// if...elif...elif...else, which is why we did
// the ''divisble-by-both'' check first.
| x when x%5=0 -> "Buzz"
| x -> string x)
// "string" is a function that converts its argument
// to a string. F# is statically-typed, so all the
// patterns have to evaluate to the same type, so the
// return value of the map call can be e.g. an
// IEnumerable<string> (aka seq<string>).
|> // Another pipeline; pipe the prior sequence into...
Seq.iter // iter applies a function to every element of a
// sequence, but the function should return "unit"
// (like "void"), and iter itself returns unit.
// Whereas sequences are lazy, "iter" will "force"
// the sequence since it needs to apply the function
// to each element only for its effects.
(printfn "%s")
// F# has type-safe printing; printfn "%s" expr
// requires expr to have type string. Usual kind of
// %d for integers, etc. Here we have partially
// applied printfn, it''s a function still expecting
// the string, so this is a one-argument function
// that is appropriate to hand to iter. Hurrah!
Actualmente estoy aprendiendo F # y he probado (un extremadamente) simple ejemplo de FizzBuzz.
Este es mi intento inicial:
for x in 1..100 do
if x % 3 = 0 && x % 5 = 0 then printfn "FizzBuzz"
elif x % 3 = 0 then printfn "Fizz"
elif x % 5 = 0 then printfn "Buzz"
else printfn "%d" x
¿Qué soluciones podrían ser más elegantes / simples / mejores (explicando por qué) usando F # para resolver este problema?
Nota: El problema de FizzBuzz está pasando por los números 1 a 100 y cada múltiplo de 3 impresiones Fizz, cada múltiplo de 5 impresiones Buzz, cada múltiplo de 3 y 5 copias FizzBuzz. De lo contrario, se muestra el número simple.
Gracias :)
Sin embargo, una solución en estilo F # (es decir, con el uso de Patrones Activos):
let (|P3|_|) i = if i % 3 = 0 then Some i else None
let (|P5|_|) i = if i % 5 = 0 then Some i else None
let f = function
| P3 _ & P5 _ -> printfn "FizzBuzz"
| P3 _ -> printfn "Fizz"
| P5 _ -> printfn "Buzz"
| x -> printfn "%d" x
Seq.iter f {1..100}
//or
for i in 1..100 do f i
Mi ejemplo es solo una pequeña mejora con respecto al código publicado por ''ssp''. Utiliza patrones activos parametrizados (que toman el divisor como argumento). Aquí hay una explicación más detallada:
A continuación, se define un patrón activo que luego podemos utilizar en la expresión de match
para comprobar si un valor i
es divisible por un divisor
valores. Cuando escribimos:
match 9 with
| DivisibleBy 3 -> ...
... significa que el valor ''9'' pasará a la siguiente función como i
y el valor 3
pasará como divisor
. El nombre (|DivisibleBy|_|)
es una sintaxis especial, lo que significa que estamos declarando un patrón activo (y el nombre puede aparecer en la match
en el lado izquierdo de ->
. The |_|
bit significa que el patrón puede fallar (nuestro ejemplo falla cuando el valor no es divisible por divisor
)
let (|DivisibleBy|_|) divisor i =
// If the value is divisible, then we return ''Some()'' which
// represents that the active pattern succeeds - the ''()'' notation
// means that we don''t return any value from the pattern (if we
// returned for example ''Some(i/divisor)'' the use would be:
// match 6 with
// | DivisibleBy 3 res -> .. (res would be asigned value 2)
// None means that pattern failed and that the next clause should
// be tried (by the match expression)
if i % divisor = 0 then Some () else None
Ahora podemos iterar sobre todos los números y Seq.iter
con los patrones (nuestro patrón activo) usando match
(o usando Seq.iter
o alguna otra técnica como se muestra en otras respuestas):
for i in 1..100 do
match i with
// & allows us to run more than one pattern on the argument ''i''
// so this calls ''DivisibleBy 3 i'' and ''DivisibleBy 5 i'' and it
// succeeds (and runs the body) only if both of them return ''Some()''
| DivisibleBy 3 & DivisibleBy 5 -> printfn "FizzBuzz"
| DivisibleBy 3 -> printfn "Fizz"
| DivisibleBy 5 -> printfn "Buzz"
| _ -> printfn "%d" i
Para obtener más información sobre los patrones activos F #, aquí hay un enlace de documentación de MSDN . Creo que si elimina todos los comentarios, el código será un poco más legible que la versión original. Muestra algunos trucos bastante útiles :-), pero en tu caso, la tarea es relativamente fácil ...
Para agregar una respuesta más, aquí hay otro enfoque sin coincidencia de patrones. Utiliza el hecho de que Fizz + Buzz = FizzBuzz
, por lo que en realidad no necesita probar los tres casos, solo necesita ver si es divisible por 3 (luego imprimir "Fizz") y también ver si es divisible por 5 (luego imprima "Buzz") y, finalmente, imprima una nueva línea:
for i in 1..100 do
for divisor, str in [ (3, "Fizz"); (5; "Buzz") ] do
if i % divisor = 0 then printf str
printfn ""
El ciclo for
anidado asigna 3 y "Fizz" a divisor
y str
en la primera iteración y luego al segundo par de valores en la segunda iteración. El beneficio es que puede agregar fácilmente la impresión de "Jezz" cuando el valor es divisible por 7 :-) ... ¡en caso de que la extensibilidad de la solución sea una preocupación!
Aquí está mi versión:
//initialize array a with values from 1 to 100
let a = Array.init 100 (fun x -> x + 1)
//iterate over array and match *indexes* x
Array.iter (fun x ->
match x with
| _ when x % 15 = 0 -> printfn "FizzBuzz"
| _ when x % 5 = 0 -> printfn "Buzz"
| _ when x % 3 = 0 -> printfn "Fizz"
| _ -> printfn "%d" x
) a
Este es mi primer programa en F #.
No es perfecto, pero creo que alguien que comienza a aprender F # (como yo :) puede descubrir lo que sucede aquí bastante rápido.
Sin embargo, me pregunto ¿cuál es la diferencia entre la coincidencia con cualquier _
o x
en la coincidencia de patrones anterior?
Aquí hay una versión que hace hincapié en una lista tuple genérica de carbonaciones:
let carbonations = [(3, "Spizz") ; (5, "Fuzz"); (15, "SpizzFuzz");
(30, "DIZZZZZZZZ"); (18, "WHIIIIII")]
let revCarbonated = carbonations |> List.sort |> List.rev
let carbonRoute someCarbonations findMe =
match(List.tryFind (fun (x,_) -> findMe % x = 0) someCarbonations) with
| Some x -> printfn "%d - %s" findMe (snd x)
| None -> printfn "%d" findMe
let composeCarbonRoute = carbonRoute revCarbonated
[1..100] |> List.iter composeCarbonRoute
No pude encontrar una solución funcional que no incluyera pruebas para i% 15 = 0 . Siempre sentí que no hacer pruebas para eso es parte de esta asignación "estúpida". Tenga en cuenta que esto probablemente no es idiomático F # ya que es mi primer programa en el idioma.
for n in 1..100 do
let s = seq {
if n % 3 = 0 then yield "Fizz"
if n % 5 = 0 then yield "Buzz" }
if Seq.isEmpty s then printf "%d"n
printfn "%s"(s |> String.concat "")
Aquí hay uno más:
let fizzy num =
match num%3, num%5 with
| 0,0 -> "fizzbuzz"
| 0,_ -> "fizz"
| _,0 -> "buzz"
| _,_ -> num.ToString()
[1..100]
|> List.map fizzy
|> List.iter (fun (s:string) -> printfn "%s" s)
Encuentro que esto es un poco más legible. La respuesta editada fue inspirada un poco por los otros.
let FizzBuzz n =
match n%3,n%5 with
| 0,0 -> "FizzBuzz"
| 0,_ -> "Fizz"
| _,0 -> "Buzz"
| _,_ -> string n
[1..100]
|> Seq.map (fun n -> FizzBuzz n)
|> Seq.iter (printfn "%s")
No me gustan todas estas cadenas repetidas, aquí está el mío:
open System
let ar = [| "Fizz"; "Buzz"; |]
[1..100] |> List.map (fun i ->
match i % 3 = 0, i % 5 = 0 with
| true, false -> ar.[0]
| false, true -> ar.[1]
| true, true -> ar |> String.Concat
| _ -> string i
|> printf "%s/n"
)
|> ignore