【问题标题】:F# assuming int when actually dealing with int64F# 在实际处理 int64 时假设为 int
【发布时间】:2013-02-07 03:17:44
【问题描述】:

通过 Project Euler 尝试学习 F#,我在为 problem 3 编写解决方案时偶然发现了一个类型推断问题。

这是我写的:

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

但是,编译器给了我以下错误:

错误 FS0001:此表达式应为 int 类型,但此处为 int64 类型

由于我希望 findLargestPrimeFactor 中使用的类型是从使用中推断出来的,我很惊讶地发现编译器似乎假设参数 n 是一个 int,因为在唯一调用函数时完成使用 int64。

谁能给我解释一下:

  1. 为什么编译器似乎对类型感到困惑
  2. 如何解决此限制

【问题讨论】:

    标签: f# type-inference


    【解决方案1】:

    findLargestPrimeFactor 中的类型是根据使用情况推断出来的。 F#编译器以自上而下的方式进行类型推断,因此pn的类型(findLargestPrimeFactor的参数)是根据它们在函数中的使用情况推断出来的。当编译器看到let result = ... 时,参数类型已经被推断为int

    最简单的解决方案是在所有常量值上使用L 后缀,因此类型将被推断为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
    

    如果您想要更高级的解决方案,您可以使用LanguagePrimitives 模块中的通用一和零常量。这允许findLargestPrimeFactor 是通用的(-ish),因此可以更轻松地与不同的数字类型重用:

    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
    

    根据@kvb 的建议,以下是通用编写此函数的方法:

    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
    

    【讨论】:

    • 有没有办法编写一个函数以便它可以与任意数量的类型一起使用?例如如果我要编写自己的 sqrt(出于某种深不可测的原因),我希望它可以与 intfloatbigintdecimal 等一起使用。
    • 请注意,您可以通过将 findLargestPrimeFactor 设为内联并将递归移动到嵌套的 let 中来使 findLargestPrimeFactor 实际上是通用的。
    • 排序——取决于你想实现什么,你也许可以使用static type constraints
    • @kvb 我很想看看你建议的解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多