【问题标题】:F# - Why can’t I NOT use a pipeline operator in this code?F# - 为什么我不能在这段代码中使用管道运算符?
【发布时间】:2020-11-17 15:54:59
【问题描述】:

我可能在这里遗漏了一些非常基本的东西,但我不知道它是什么。

我有一些非常基本的代码如下:

let myList = [1; 3; 5; 7; 5; 3; 9; 5; 2; 5; 3; 8]
let fiveOrLess = myList |> List.choose (fun number ->
    match number with
    | number when number <= 5 -> Some number
    | _ -> None)

……正如预期的那样,效果很好。

但是,使用下面的代码,我将“myList”从管道移动到只是一个“普通”参数,我从编译器收到两条错误消息:

let myList = [1; 3; 5; 7; 5; 3; 9; 5; 2; 5; 3; 8]
let fiveOrLess = List.choose (fun number ->
    match number with
    | number when number <= 5 -> Some number
    | _ -> None) myList

错误 FS3118 值或函数定义不完整。如果这是在表达式中,则表达式的主体必须缩进到与“let”关键字相同的列。 [第二个'let'出现这个错误。]

错误 FS0010 绑定中有意外的标识符。在此点或其他标记处或之前预期不完整的结构化构造。 [最后一个“myList”上的这个错误。]

这些错误消息对我来说都很有意义,因为我所做的只是将参数“从一侧移动到另一侧”。

此处的文档:https://msdn.microsoft.com/visualfsharpdocs/conceptual/list.choose%5b%27t%2c%27u%5d-function-%5bfsharp%5d 显示的代码与我的第二个示例具有非常相似的形式,但可以正常工作。

谁能解释我在这里犯了什么愚蠢的基本错误? 另外,可能更重要的是,谁能解释一下错误消息的含义以及为什么我会得到它们,无论是在一般情况下还是在这种情况下?

【问题讨论】:

    标签: parameters f# pipeline


    【解决方案1】:

    这是一个非常微妙的格式问题,但似乎在多行 lambda(从 let 行开始)之后立即将参数放在同一行上是无效的。您的代码以几种不同的方式具有非标准格式,但 F# 编译器相当宽松,直到最后一部分解析器完全失去了对事物的跟踪并且无法提供有用的错误。

    所以并不是您的格式明显“错误”,但是一旦您养成了按照正常格式标准编写 F# 代码的习惯,您就永远不会遇到这个问题。以下是格式化函数的一些标准方法:

    let fiveOrLess =
        List.choose
            (fun number ->
                match number with
                | number when number <= 5 -> Some number
                | _ -> None)
            myList
    

    上面的缩进清楚地表明了List.choose 的两个参数是什么。


    let fiveOrLess =
        myList
        |> List.choose (fun number ->
            match number with
            | number when number <= 5 -> Some number
            | _ -> None)
    

    我们在上面使用管道,因此多行 lambda 是最后一个参数。

    【讨论】:

    • 啊,我明白了。以这种方式格式化使它看起来更清晰。谢谢你。我按照我的方式格式化它的唯一原因是因为它在 Microsoft 文档中是这样格式化的(参见我上面给出的链接)和我想从中学习的书。在我给出的链接中,微软文档在多行 lambda 之后的同一行上有参数。该文档的格式是否错误?如果是这样,那将使学习变得更加困难。附言有没有办法让编译器更加宽松,以便我可以更早地看到更多潜在问题?
    • @GarryP 是的,我现在看到在那种情况下文档不是很好。要更改编译器对空格的响应方式,您唯一可以做的就是忽略特定警告或将它们视为错误。在这种情况下它不会有帮助。
    • 我打算重新格式化我写的其他东西,看看它的样子。希望我会习惯一些事情,并且以更好的方式来做这件事将成为第二天性。再次感谢您的时间和建议。
    【解决方案2】:

    你面临的问题是缩进。

    尝试进一步缩进,如下所示:

    let myList = [1; 3; 5; 7; 5; 3; 9; 5; 2; 5; 3; 8]
    let fiveOrLess = List.choose (fun number ->
          match number with
          | number when number <= 5 -> Some number
          | _ -> None) myList
    

    另外,请注意,在这种情况下,您可以使用 function 关键字

    let myList = [1; 3; 5; 7; 5; 3; 9; 5; 2; 5; 3; 8]
    let fiveOrLess = List.choose (function
            | number when number <= 5 -> Some number
            | _ -> None) myList
    

    最后,除非这是一个简化的重现,否则filter 是您在这种情况下的功能:

    let myList = [1; 3; 5; 7; 5; 3; 9; 5; 2; 5; 3; 8]
    let fiveOrLess = List.filter (fun n -> n <= 5) myList
    

    【讨论】:

    • 对不起,我的原始代码有默认的 4 空格缩进,在粘贴/格式化到本网站时没有出现。我已经更改了上面的代码。 4 空格缩进适用于第一个版本但不适用于第二个版本,5 个空格缩进适用于第一个版本但不适用于第二个版本, 6 个空格缩进适用于两者。此外,所有缩进行上的 4 个空格缩进,除了最后一个有 6 个空格缩进的行也可以。看下一条评论……
    • 我已经阅读了 fsharpforfunandprofit.com/posts/fsharp-syntaxgithub.com/fsharp/fslang-suggestions/issues/470 但仍然看不出我做错了什么。将“fiveOrLess”重命名为“fiveOrLe”可以解决问题,但我不明白为什么。是否有某种我没有看到的模式?附言感谢“功能”提示,我还需要尝试了解“选择”与“过滤”有何不同(我只是按照 Microsoft 文档示例)。
    • @GarryP,您面临的问题是由于“缩进松弛”。官方的规则是与fun|&gt;(或任何运算符)对齐,但最近放宽了几条规则。运算符在下一行引入了一个小的负缩进,这有助于按文本对齐,而不是运算符。这条规则有点模糊,在您的示例中,它与fun 上的另一个放松规则相结合,这通常会引入一个新的“缩进范围”。我认为您的情况是一个错误,尽管是一个小错误,因为文档页面显示它应该可以工作。您可以将其添加到问题 #470。
    • 谢谢@Abel。似乎我要学习的基本知识比我预期的要多得多,比如格式化。我认为我现在还不愿意在 Github 上添加关于 issue #470 的对话。作为一个初学者,聊天对我来说变得很复杂,我不想看起来我只是把东西扔到桌子上然后跑掉了。也许当我有更多经验并且可以处理一些回复时。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-08
    • 1970-01-01
    • 2014-11-29
    • 1970-01-01
    • 1970-01-01
    • 2019-06-17
    相关资源
    最近更新 更多