【问题标题】:F#: warning FS0020: This expression should have type 'unit', but has type 'bool'F#:警告 FS0020:此表达式应具有“单位”类型,但具有“布尔”类型
【发布时间】:2009-07-10 10:42:40
【问题描述】:

我正在尝试通过一些欧拉问题来学习 F#,但我发现了一个我无法弄清楚的问题。这是我幼稚的解决方案。

let compute =
    let mutable f = false
    let mutable nr = 0
    while f = false do
        nr <- nr + 20
        f = checkMod nr
    nr

当我这样做时,我收到错误消息警告 FS0020:此表达式应具有类型“单位”,但在表达式“nr

我正在使用 VS2010 Beta 编写这篇文章。

【问题讨论】:

    标签: f# unit-type


    【解决方案1】:

    由于我可以想象这个 weg 页面成为查找有关 警告 FS0020 信息的“规范”位置,因此我简要总结了三种最常见的警告情况,以及如何处理修复它们。

    故意丢弃仅因其副作用而调用的函数的结果:

    // you are calling a function for its side-effects, intend to ignore result    
    let Example1Orig() =
        let sb = new System.Text.StringBuilder()
        sb.Append("hi")       // warning FS0020
        sb.Append(" there")   // warning FS0020
        sb.ToString()
    
    let Example1Fixed() =
        let sb = new System.Text.StringBuilder()
        sb.Append("hi") |> ignore
        sb.Append(" there") |> ignore
        sb.ToString()
    

    警告很有用,指出错误(函数没有效果):

    // the warning is telling you useful info 
    // (e.g. function does not have an effect, rather returns a value)
    let Example2Orig() =
        let l = [1;2;3] 
        List.map (fun x -> x * 2) l    // warning FS0020
        printfn "doubled list is %A" l
    
    let Example2Fixed() =
        let l = [1;2;3] 
        let result = List.map (fun x -> x * 2) l
        printfn "doubled list is %A" result
    

    令人困惑的赋值运算符和相等比较运算符:

    // '=' versus '<-'
    let Example3Orig() =
        let mutable x = 3
        x = x + 1          // warning FS0020
        printfn "%d" x    
    
    let Example3Fixed() =
        let mutable x = 3
        x <- x + 1
        printfn "%d" x    
    

    【讨论】:

    • 获得此错误的另一种令人讨厌的方法是忘记破折号:x
    【解决方案2】:

    下面一行:

    f = checkMod nr
    

    是一个平等检查,而不是我相信你想要的分配。将其更改为:

    f <- checkMod nr
    

    一切都应该可以正常工作。我不确定你为什么在上一行使用了正确的语法,而不是那一行...

    另外,while f = false do 行真的应该简化为while not f do;布尔值的相等性检查相当复杂。

    正如我的旁注,我觉得有必要指出您正在有效地尝试将 F# 用作命令式语言。在函数式语言(包括 F#)中强烈建议不要使用可变变量和 while 循环,尤其是在存在纯函数式(和更简单)的解决方案时,例如在这种情况下。我建议您阅读一些有关函数式编程的知识。当然,掌握语法本身就是一件有用的事情。

    【讨论】:

    • 谢谢,解决了这个问题,不敢相信我看了这么久就错过了:) 我的目标是学习函数式编程风格,但是没有时间学习它是一个非常慢过程。
    • 是的,很公平。函数式编程对于以前只使用命令式语言(例如 C/C#/Java)的程序员来说往往是一个巨大的冲击。它需要一种完全不同的思维方式,可以随着时间的推移而学习。
    • 但 F# 的一大优点是,您可以从熟悉的编码风格(命令式)开始,轻松进入功能性思维模式,同时仍然保持高效。
    • 您可以编写命令式代码这一事实允许您优化代码的一小部分,而无需使用其他语言。
    • @gradbot:确实如此——尽管只有当 a) 所有功能等价物(通常至少有几个)效率较低,并且 b) 效率/性能在这种情况下很重要时,才会出现这种情况。 @James Hugard:这是一个公平的观点,尽管我倾向于建议直接研究功能性的东西并强迫自己以适当的方式思考是最有用的。至少为我工作。 :)
    【解决方案3】:

    如果您尝试采用函数式风格,请尽量避免可变值。

    例如这样:

    let nr =
       let rec compute nr =  
          if checkMod nr then nr else compute (nr + 20)
       compute 0     
    

    【讨论】:

      【解决方案4】:

      while expressions in F# 如果您来自命令式语言,请稍微适应一下。 while 表达式中的每一行都必须计算为 unit(想想 C++/C# 中的 void)。然后,整个表达式的计算结果也为 unit

      在示例中:

      nr <- nr + 20
      

      计算为unit

      f = checkMod nr
      

      评估为bool,如Noldorin 所述。这会导致报告警告消息。如果您愿意,您实际上可以关闭警告。只需将以下内容放在文件顶部:

      #nowarn "0020"
      

      【讨论】:

        【解决方案5】:

        我已经使用命令式编程很长时间了,所以习惯函数式编程思维需要一段时间。

        在您的示例中,您试图找到通过 checkMod 测试的 20 的第一个倍数。这就是 what 部分。对于功能 how 部分,我建议浏览可用于序列的方法。您需要的是通过测试的序列的第一个元素(20 的倍数),如下所示:

        let multi20 = Seq.initInfinite (fun i -> i*20)
        let compute = multi20 |> Seq.find checkMod
        

        第一个 let 生成一个无限的二十个列表(我做了一个)。第二个让找到所述列表中通过您的测试的第一个数字。你的任务是确保确实有一个数字可以通过测试,但对于命令式代码当然也是如此。

        如果你想把上面两行浓缩成一条,也可以这样写

        let computeCryptic = Seq.initInfinite ((*) 20) |> Seq.find checkMod
        

        但我发现在代码中使用类似的特技可能会导致几周后尝试阅读它时感到头疼。

        【讨论】:

          【解决方案6】:

          本着与 Brian 的帖子相同的精神,这是获得警告 FS0020 的另一种方法:简而言之,我不小心对函数参数进行了元组处理。

          作为一个 F# 新手,我很难调试下面的代码,第二行 (let gdp...) 给出了警告 FS0020: This expression should have type 'unit', but has type '(string -> ^a -> 单位) * 字符串 * 浮点数'。事实证明,这条线根本不是问题。相反,是 printfn 行搞砸了。从参数列表中删除逗号分隔符修复了它。

          for country in wb.Regions.``Arab World``.Countries do
            let gdp = country.Indicators.``GDP per capita (current US$)``.[2010]
            let gdpThous = gdp / 1.0e3
            printfn "%s, %s (%.2f)" country.Name, country.CapitalCity, gdpThous
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-03-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多