为什么这个F#函数需要括号?

时间:2016-09-02 14:31:04

标签: f# functional-programming let

为什么下面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。这是一个很好的例子。

如果没有parens,我可以处理各种各样的函数对象吗?

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

3 个答案:

答案 0 :(得分:22)

似乎来自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

答案 1 :(得分:6)

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

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

答案 2 :(得分:4)

你的问题的答案是:

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->bool的绑定,即带有函数值的绑定,其中函数不接受任何输入并返回bool值。