F#:警告FS0020:这个表达式应该有'unit'类型,但是类型'bool'

时间:2022-07-14 16:10:27

I'm trying to learn F# by going through some of the Euler problems and I found an issue I haven't been able to figure out. This is my naive solution.

我试图通过解决一些欧拉问题来学习F#,我发现了一个我无法弄清楚的问题。这是我天真的解决方案。

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

When i do this I get the error message warning FS0020: This expression should have type 'unit', but has type 'bool' on the expression "nr <- nr +20". I've tried rewriting and moving the expressions around and I always get that error on the line below the while statement.

当我这样做时,我收到错误消息警告FS0020:该表达式应该具有类型'unit',但在表达式“nr < - nr +20”上具有类型'bool'。我已经尝试重写和移动表达式,我总是在while语句下面的行上得到错误。

I'm writing this using VS2010 Beta.

我是用VS2010 Beta写的。

6 个解决方案

#1


The following line:

以下行:

f = checkMod nr

is an equality check, not an assignment as I believe you are intending. Change it to:

是一个平等检查,而不是我认为你打算的任务。将其更改为:

f <- checkMod nr

and all should work fine. I'm not sure why you've used the correct syntax on the previous line and not that line...

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

Also, the line while f = false do should really be simplified to while not f do; equality checks on booleans are rather convoluted.

此外,f = false时的行应该真正简化为f而不是f do;对布尔值的平等检查相当复杂。

As I side note, I feel a need to point out that you are effectively trying to use F# as an imperative language. Use of mutable variables and while loops are strongly discouraged in functional languages (including F#), especially when a purely functional (and simpler) solution exists, as in this situation. I recommend you read up a bit on programming in the functional style. Of course, just getting to grips with the syntax is a useful thing in itself.

我个人注意到,我觉得有必要指出你有效地尝试使用F#作为命令式语言。在功能语言(包括F#)中强烈建议不要使用可变变量和while循环,特别是在存在纯功能(和更简单)的解决方案时,如在这种情况下。我建议你阅读一下功能风格的编程。当然,只是掌握语法本身就是一件有用的事情。

#2


Since I can imagine this weg page becoming the 'canonical' place to look up information about warning FS0020, here's my quick summary of the three commonest cases in which you get the warning, and how to fix them.

由于我可以想象这个weg页面成为查找有关警告FS0020的信息的“规范”位置,这里是我收到警告的三个最常见情况的快速摘要,以及如何解决它们。

Intentionally discarding the result of a function that is called only for its side-effects:

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

// 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()

Warning is useful, pointing out an error (function has no effects):

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

// 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

Confusing assignment operator and equality comparison operator:

混淆赋值运算符和相等比较运算符:

// '=' 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    

#3


If you're trying to adopt the functional style, try to avoid mutable values.

如果您尝试采用功能样式,请尝试避免使用可变值。

For example like this:

例如这样:

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

#4


while expressions in F# take a little getting used to if you're coming from an imperative language. Each line in a while expression must evaluate to unit (think void from C++/C#). The overall expression then also evaluates to unit.

如果你来自命令式语言,F#中的表达需要一点点习惯。 while表达式中的每一行必须求值为unit(从C ++ / C#中考虑void)。然后整体表达式也评估为单位。

In the example:

在示例中:

nr <- nr + 20

evaluates to unit whereas

评估单位而不是

f = checkMod nr

evaluates to a bool as Noldorin noted. This results in a warning message being reported. You can actually turn the warning off if you so desire. Just put the following at the top of your file:

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

#nowarn "0020"

#5


I've been programming in an imperative style for a long time, so getting used to the functional programming mindset took a while.

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

In your example, you're trying to find the first multiple of 20 that passes your checkMod test. That's the what part. For the functional how part, I recommend browsing through the methods available to sequences. What you need is the first element of a sequence (multiples of 20) passing your test, like this:

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

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

The first let generates an infinite list of twentyples (I made that one up). The second let finds the first number in said list that passes your test. Your task is to make sure that there actually is a number that will pass the test, but that's of course also true for the imperative code.

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

If you want to condense the two above lines into one, you can also write

如果你想将上面两行压缩成一行,你也可以写

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

but I find that pulling stunts like that in code can lead to headaches when trying to read it a few weeks later.

但我发现在几周后尝试阅读它时,在代码中拉动这样的特技会导致头痛。

#6


In the same spirit as Brian's post, here is another way to get warning FS0020: In a nutshell, I accidentally tupled the function arguments.

与Brian的帖子一样,这是获得警告FS0020的另一种方法:简而言之,我不小心弄乱了函数参数。

Being an F# newbie, I had a difficult time debugging the code below, which for the second line (let gdp...) gave the warning FS0020: This expression should have type 'unit', but has type '(string -> ^a -> unit) * string * float'. It turns out that line was not the problem at all; instead, it was the printfn line that was messed up. Removing the comma separators from the argument list fixed it.

作为一个F#新手,我很难调试下面的代码,对于第二行(让gdp ...)给出警告FS0020:这个表达式应该有'unit'类型,但是类型'(string - > ^ a - > unit)* string * float'。事实证明,线路根本不是问题;相反,它是打印出来的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

#1


The following line:

以下行:

f = checkMod nr

is an equality check, not an assignment as I believe you are intending. Change it to:

是一个平等检查,而不是我认为你打算的任务。将其更改为:

f <- checkMod nr

and all should work fine. I'm not sure why you've used the correct syntax on the previous line and not that line...

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

Also, the line while f = false do should really be simplified to while not f do; equality checks on booleans are rather convoluted.

此外,f = false时的行应该真正简化为f而不是f do;对布尔值的平等检查相当复杂。

As I side note, I feel a need to point out that you are effectively trying to use F# as an imperative language. Use of mutable variables and while loops are strongly discouraged in functional languages (including F#), especially when a purely functional (and simpler) solution exists, as in this situation. I recommend you read up a bit on programming in the functional style. Of course, just getting to grips with the syntax is a useful thing in itself.

我个人注意到,我觉得有必要指出你有效地尝试使用F#作为命令式语言。在功能语言(包括F#)中强烈建议不要使用可变变量和while循环,特别是在存在纯功能(和更简单)的解决方案时,如在这种情况下。我建议你阅读一下功能风格的编程。当然,只是掌握语法本身就是一件有用的事情。

#2


Since I can imagine this weg page becoming the 'canonical' place to look up information about warning FS0020, here's my quick summary of the three commonest cases in which you get the warning, and how to fix them.

由于我可以想象这个weg页面成为查找有关警告FS0020的信息的“规范”位置,这里是我收到警告的三个最常见情况的快速摘要,以及如何解决它们。

Intentionally discarding the result of a function that is called only for its side-effects:

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

// 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()

Warning is useful, pointing out an error (function has no effects):

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

// 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

Confusing assignment operator and equality comparison operator:

混淆赋值运算符和相等比较运算符:

// '=' 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    

#3


If you're trying to adopt the functional style, try to avoid mutable values.

如果您尝试采用功能样式,请尝试避免使用可变值。

For example like this:

例如这样:

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

#4


while expressions in F# take a little getting used to if you're coming from an imperative language. Each line in a while expression must evaluate to unit (think void from C++/C#). The overall expression then also evaluates to unit.

如果你来自命令式语言,F#中的表达需要一点点习惯。 while表达式中的每一行必须求值为unit(从C ++ / C#中考虑void)。然后整体表达式也评估为单位。

In the example:

在示例中:

nr <- nr + 20

evaluates to unit whereas

评估单位而不是

f = checkMod nr

evaluates to a bool as Noldorin noted. This results in a warning message being reported. You can actually turn the warning off if you so desire. Just put the following at the top of your file:

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

#nowarn "0020"

#5


I've been programming in an imperative style for a long time, so getting used to the functional programming mindset took a while.

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

In your example, you're trying to find the first multiple of 20 that passes your checkMod test. That's the what part. For the functional how part, I recommend browsing through the methods available to sequences. What you need is the first element of a sequence (multiples of 20) passing your test, like this:

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

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

The first let generates an infinite list of twentyples (I made that one up). The second let finds the first number in said list that passes your test. Your task is to make sure that there actually is a number that will pass the test, but that's of course also true for the imperative code.

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

If you want to condense the two above lines into one, you can also write

如果你想将上面两行压缩成一行,你也可以写

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

but I find that pulling stunts like that in code can lead to headaches when trying to read it a few weeks later.

但我发现在几周后尝试阅读它时,在代码中拉动这样的特技会导致头痛。

#6


In the same spirit as Brian's post, here is another way to get warning FS0020: In a nutshell, I accidentally tupled the function arguments.

与Brian的帖子一样,这是获得警告FS0020的另一种方法:简而言之,我不小心弄乱了函数参数。

Being an F# newbie, I had a difficult time debugging the code below, which for the second line (let gdp...) gave the warning FS0020: This expression should have type 'unit', but has type '(string -> ^a -> unit) * string * float'. It turns out that line was not the problem at all; instead, it was the printfn line that was messed up. Removing the comma separators from the argument list fixed it.

作为一个F#新手,我很难调试下面的代码,对于第二行(让gdp ...)给出警告FS0020:这个表达式应该有'unit'类型,但是类型'(string - > ^ a - > unit)* string * float'。事实证明,线路根本不是问题;相反,它是打印出来的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