F#suponiendo int cuando en realidad se trata de int64
type-inference (1)
Al pasar por el Proyecto Euler tratando de aprender F #, me encontré con lo que parece ser un problema de inferencia de tipo al escribir una solución para el problema 3 .
Esto es lo que escribí:
let rec findLargestPrimeFactor p n =
if n = 1 then p
else
if n % p = 0 then findLargestPrimeFactor p (n/p)
else findLargestPrimeFactor (p+1) n
let result = findLargestPrimeFactor 2 600851475143L
Sin embargo, el compilador me da el siguiente error:
error FS0001: This expression was expected to have type int but here has type int64
Como espero que los tipos utilizados en findLargestPrimeFactor
del uso, me sorprende descubrir que el compilador parece suponer que el parámetro n
es una int, ya que en la única llamada a la función se hace con un int64.
¿Podría alguien explicarme:
- por qué el compilador parece estar confundido acerca de los tipos
- cómo evitar esta limitación
Los tipos en findLargestPrimeFactor
se deducen del uso. El compilador F # realiza la inferencia de tipo de arriba a abajo, de modo que los tipos de p
y n
(los parámetros de findLargestPrimeFactor
) se deducen de su uso en la función. Para cuando el compilador ve el let result = ...
, los tipos de parámetros ya se han inferido como int
.
La solución más fácil es usar el sufijo L
en todos sus valores constantes, por lo que los tipos se int64
como int64
:
let rec findLargestPrimeFactor p n =
if n = 1L then p
else
if n % p = 0L then findLargestPrimeFactor p (n/p)
else findLargestPrimeFactor (p + 1L) n
let result = findLargestPrimeFactor 2L 600851475143L
Si desea una solución más elegante, puede usar las constantes genéricas y cero del módulo LanguagePrimitives
. Esto permite que findLargestPrimeFactor
sea genérico (-ish) para que pueda reutilizarse más fácilmente con diferentes tipos numéricos:
open LanguagePrimitives
let rec findLargestPrimeFactor p n =
if n = GenericOne then p
else
if n % p = GenericZero then findLargestPrimeFactor p (n/p)
else findLargestPrimeFactor (p + GenericOne) n
(* You can use one of these, but not both at the same time --
now the types of the _arguments_ are used to infer the types
of ''p'' and ''n''. *)
//let result = findLargestPrimeFactor 2L 600851475143L
let result = findLargestPrimeFactor 2 Int32.MaxValue
La sugerencia de Per @ kvb, así es como puedes escribir esta función genéricamente:
open LanguagePrimitives
let inline findLargestPrimeFactor p n =
let rec findLargestPrimeFactor p n =
if n = GenericOne then p
else
if n % p = GenericZero then findLargestPrimeFactor p (n/p)
else findLargestPrimeFactor (p + GenericOne) n
findLargestPrimeFactor p n
(* Now you can call the function with different argument types
as long as the generic constraints are satisfied. *)
let result = findLargestPrimeFactor 2L 600851475143L
let result'' = findLargestPrimeFactor 2 Int32.MaxValue