我正在尝试通过解决一些欧拉问题来学习F#,我发现了一个我无法弄清楚的问题。这是我天真的解决方案。
let compute =
let mutable f = false
let mutable nr = 0
while f = false do
nr <- nr + 20
f = checkMod nr
nr
当我这样做时,我收到错误消息警告FS0020:此表达式应该具有类型“unit”,但在表达式“nr&lt; - nr +20”上具有类型“bool”。我已经尝试重写和移动表达式,我总是在while语句下面的行上得到错误。
我是用VS2010测试版写的。
答案 0 :(得分:12)
因为我可以想象这个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
答案 1 :(得分:10)
以下一行:
f = checkMod nr
是一个相等检查,而不是我认为你打算的任务。将其更改为:
f <- checkMod nr
一切都应该正常。我不确定为什么你在前一行使用了正确的语法而不是那行......
此外,行while f = false do
应该真正简化为while not f do
;对布尔值的平等检查相当复杂。
正如我所说,我觉得有必要指出你是否有效地尝试使用F#作为命令式语言。在函数式语言(包括F#)中强烈建议不要使用可变变量和while循环,特别是当存在纯函数(和更简单)的解决方案时,就像在这种情况下一样。我建议你阅读一下功能风格的编程。当然,掌握语法本身就是一件有用的事情。
答案 2 :(得分:1)
如果您尝试采用功能样式,请尝试避免使用可变值。
例如:
let nr =
let rec compute nr =
if checkMod nr then nr else compute (nr + 20)
compute 0
答案 3 :(得分:0)
while
expressions需要一点点习惯。 while
表达式中的每一行必须计算为unit
(从C ++ / C#中考虑void
)。然后整体表达式也评估为unit
。
在示例中:
nr <- nr + 20
评估为unit
而
f = checkMod nr
评估为bool
,注明Noldorin。这会导致报告警告消息。如果您愿意,您实际上可以关闭警告。只需将以下内容放在文件的顶部:
#nowarn "0020"
答案 4 :(得分:0)
我长期以编程方式编程,所以习惯编程功能需要一段时间。
在您的示例中,您试图找到通过checkMod测试的20的第一个倍数。那是什么部分。对于功能如何部分,我建议浏览序列可用的方法。您需要的是通过测试的序列的第一个元素(20的倍数),如下所示:
let multi20 = Seq.initInfinite (fun i -> i*20)
let compute = multi20 |> Seq.find checkMod
第一个让我们生成一个无限的二十字形列表(我把它做了一个)。第二个让我们找到通过测试的列表中的第一个数字。你的任务是确保实际上有一个数字可以通过测试,但对于命令式代码来说当然也是如此。
如果你想将上面两行压缩成一行,你也可以写
let computeCryptic = Seq.initInfinite ((*) 20) |> Seq.find checkMod
但我发现在几周后尝试阅读时,在代码中拉动这样的特技会导致头痛。
答案 5 :(得分:0)
与Brian的帖子一样,这是另一种获得警告的方法FS0020:简而言之,我不小心弄乱了函数参数。
作为一个F#新手,我很难调试下面的代码,对于第二行(让gdp ...)发出警告FS0020:这个表达式应该有&#39; unit&#39;,但是有类型&#39;(字符串 - &gt; ^ a - &gt;单位)*字符串*浮动&#39;。事实证明,线路根本不是问题;相反,它是打印出来的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