F#(VS2010 Beta1)中的缩进问题

4

我只是在学习f#,所以可能我做了一些很愚蠢的事情。如果有相关文档,请随时指出给我,我已经搜索过了但没有找到。我正在使用Windows 7 (.Net 4.0)上的Visual Studio 2010 beta。

我的第一个f#项目进展顺利。嗯...几乎所有的东西都很好。特别是我正在编写一个非常简单的线性插值函数,代码如下:

let linterp (x:double) (xvalues:double list) (yvalues:double list) =
    let num_els = xvalues.Length
    if x <= xvalues.Head then 
        let result = yvalues.Head
    elif x >= (List.rev xvalues).Head then 
        let result = (List.rev yvalues).Head
    else for idx in [0 .. num_els] do 
        if List.nth xvalues idx >= x then 
            let x0 = xvalues.Item idx
            let y0 = yvalues.Item idx
            let x1 = xvalues.Item (idx+1)
            let y1 = yvalues.Item (idx+1)
            let result = y0 + (y1-y0)/(x1-x0)*(x - x0)
    result

我正在收到一系列错误,这些错误完全超出了我的理解能力。

以下是错误和警告列表:

  • 对于第一个、第二个和最后一个“let”,出现“返回表达式中的错误。可能的正确缩进”。

  • 对于“if”,出现“可能的缩进不正确:此令牌偏离了从位置(39:10)开始的上下文。尝试进一步缩进此令牌或使用标准格式约定。”

  • 对于最后一行(结果),出现“在此点之前或之前存在不完整的结构化结构”。

我想补充说明的是,我必须费点心思来注释类型,因为由于某种我不知道的原因,编译器能够正确地推断出第一列表中的类型,但对于第二列表,类型总是被推断为unit。此外,我的原始版本没有绑定名称result,而只是“返回表达式”,例如:

if x <= xvalues.Head then 
    yvalues.Head

或者在
else for idx in [0 .. num_els] do 
    if List.nth xvalues idx >= x then 
        let x0 = xvalues.Item idx
        let y0 = yvalues.Item idx
        let x1 = xvalues.Item (idx+1)
        let y1 = yvalues.Item (idx+1)
        y0 + (y1-y0)/(x1-x0)*(x - x0)

这会导致“for”下出现一个错误,指出“该表达式类型为unit,但在此处与double类型一起使用”,并且“if”可能缩进不正确。
我认为当我看到这个问题的解决方案时,会觉得很傻,但我已经被这个简单的问题困扰了一个多小时,所以我请求您的帮助。
提前感谢!
附言:我已经检查过选项卡是否在Tools->options->...->F#->Tabs菜单中正确地解释为空格。
附言2:这是我在SO上的第一个问题 :-)

我感到惭愧,但代码中有一个明显的偏移错误 :-) 这证明我太老了,不能在深夜编码。 为了方便起见,let x0 = xvalues.Item (idx - 1)let x1 = xvalues.Item idx,因为 idx 表示大于或等于 x 的第一项。 - Francesco
2个回答

5
let linterp x (xvalues:double list) (yvalues:double list) =
    if x <= xvalues.Head then 
        yvalues.Head
    elif x >= (List.rev xvalues).Head then 
        (List.rev yvalues).Head
    else
        let idx = List.findIndex (fun e -> e >= x) xvalues
        let x0 = xvalues.Item idx
        let y0 = yvalues.Item idx
        let x1 = xvalues.Item (idx+1)
        let y1 = yvalues.Item (idx+1)
        y0 + (y1-y0)/(x1-x0)*(x - x0)

5
你的问题是类似以下这样的内容:
let result = yvalues.Head

这不是一个完整的表达式,因此它不能构成if块中一个分支的主体。你的初始方法是正确的,只是for ... do循环不返回有意义的值(它返回(),这是类型为unit的唯一值,编译器试图解释; 这类似于C#等语言中的void)。您将需要使用具有您正在寻找的值的表达式,而不是for循环。对您的代码进行最小更改的方法是使用可变值,在循环中以命令方式设置它。更符合惯用法的方法是使用内置函数,如List.fold将列表压缩为单个值。由于您需要访问列表中的连续条目(并且需要同时操作xvalues和yvalues),这使得您可能需要使用List.zip和Seq.pairwise,这可能会降低不习惯F#的人的清晰度。

此外,还有一些其他更改可以应用于使您的代码更符合惯用法。例如,let x0 = xvalues.Item idx通常会写成let x0 = xvalues.[idx]。但是,请注意,F#列表是不可变的链接列表,因此不支持快速随机访问。这是另一个原因,支持使用内置的List运算符的方法。


谢谢,现在我更好地理解了错误的含义。例如,如果我没有使用#light语法,我应该写if x <= xvalues.Head then let result = yvalues.Head in而“in”会提醒我在局部范围内绑定名称并且缺少表达式(我正在阅读f#规范的第6.7节“绑定表达式”)。在我的看来,for ... do循环执行其副作用,是吗?感谢您对更具惯用性的代码的建议。我还在学习中,所以还非常笨拙。我将尝试使用Seq.pairwise的方法。 - Francesco

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接