【问题标题】:Why are parentheses needed on this F# function?为什么这个 F# 函数需要括号?
【发布时间】:2017-01-10 17:11:41
【问题描述】:

为什么下面的read_rest_of_csv 需要括号?

    let read_rest_of_csv() =
        csv_data.Add(csv_fileH.ReadFields()) |> ignore
        not csv_fileH.EndOfData

    while read_rest_of_csv() do ignore None

没有括号,循环不会终止。

open System
open System.Threading
open System.Collections.Generic
open System.Linq
open System.Text
open System.Threading.Tasks
open System.IO
open Microsoft.VisualBasic.FileIO

[<EntryPoint>]
let main argv =
    let csv_fileH = new TextFieldParser("test1.csv")
    csv_fileH.TextFieldType = FieldType.Delimited |> ignore
    let x = csv_fileH.SetDelimiters(",")
    let csv_data = new List<string[]>()

    let eod = csv_fileH.EndOfData
    if not eod then
        let column_headings = csv_fileH.ReadFields()
        csv_data.Add(column_headings) |> ignore

        let read_rest_of_csv =
            csv_data.Add(csv_fileH.ReadFields()) |> ignore
            not csv_fileH.EndOfData

        while read_rest_of_csv do ignore None

    0 

抱歉,我不记得我在哪里看到的了。我认为它在SO中。这是一个很好的例子。

这可能是因为没有括号我正在处理某种函数对象吗?

我确实不仅来自 C、C++ 和 C# 背景,而且还具有中级 Clojure 背景。在我使用 F# 语法的情况下,更详细地阅读我的 Haskell 手册可能会有所帮助,因为语法看起来很相似。

【问题讨论】:

    标签: f# functional-programming let


    【解决方案1】:

    似乎来自 C 系列语言(C#、Java、C、C++、JavaScript)的人在理解 F# 中括号的使用方面存在问题。我当然有,而且我花了几年的时间来了解事情是如何运作的。

    简而言之,F# 中最基本的构建块是。值可以是let-bound:

    let foo = bar
    

    这意味着foo 是一个值,恰好等于bar

    函数也是值:

    // 'a -> 'a * 'a
    let f = fun x -> x, x
    

    这里,f 是一个函数,它接受一些值 (x) 并返回一个以 x 作为第一个和第二个元素的元组。

    写起来有点麻烦,所以有一个简写:

    // 'a -> 'a * 'a
    let f x = x, x
    

    请注意,这些表达式中没有括号。

    有时您需要调整运算符的优先级。就像在数学中一样,1 + 2 * 3(相当于1 + (2 * 3))与(1 + 2) * 3 不同。在 F# 中,您还可以使用方括号来覆盖优先级。因此

    // 'a -> string * 'a
    let f x = someOtherFunction x, x
    

    不一样

    // x:'a -> string
    let f x = someOtherFunction (x, x)
    

    (在这种情况下,someOtherFunction 是一个返回 string 的函数。)

    请注意,括号不表示函数调用;它们只是为了控制评估顺序。

    有时,您想定义一个不接受任何输入的函数。但是,您不能这样定义它:

    let f = whatever
    

    因为这会使它成为一个 ,它立即被 let 绑定到 whatever。相反,您可以让函数采用内置类型unit 的值。这个类型只有一个值,写成()

    let f () = whatever
    

    这意味着f 是一个模式匹配其输入与unit 唯一已知值的函数。

    每当您使用() 调用f 时,都会计算并返回表达式whatever

    【讨论】:

    • 您对人们的编程背景进入 F# 提出了很好的观点。因此,我在 OP 中添加了更多内容以表明我的背景。我将不得不使用的部分是 unit、() 和那些绑定之类的东西。
    【解决方案2】:

    你的问题的答案是:

    let read_rest_of_csv =
        csv_data.Add(csv_fileH.ReadFields()) |> ignore
        not csv_fileH.EndOfData
    

    根本不是一个函数。这与:

    > let i = 1;;
    
    val i : int = 1
    

    这声明了一个具有整数值的绑定。如果你想声明一个不带参数的函数值的绑定,看起来像这样:

    > let i () = 1;;
    
    val i : unit -> int
    

    同样的推理也适用于read_rest_of_csv。如果没有括号,则声明类型为bool 的绑定。使用括号,您声明了一个类型为 unit-&gt;bool 的绑定,即与函数值的绑定,其中函数不接受输入并返回布尔值。

    【讨论】:

      【解决方案3】:

      如果没有括号,内容将执行一次且不再执行。 read_rest_of_csv 的类型是bool:你基本上是在说while true do ignore None

      括号表示read_rest_of_csv 的类型为unit -&gt; bool,因此每次调用它时,它都会读取一行并移动光标。否则,它只会执行一次。

      【讨论】:

      • 我可以用括号代替括号吗?
      • 对于“括号”,请阅读“括号”。我会修改我的答案(恐怕不会让孩子失望)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-11-07
      • 2019-11-13
      • 2020-11-15
      • 2012-08-03
      • 2014-08-09
      • 1970-01-01
      • 2013-06-16
      相关资源
      最近更新 更多