F# 如何调用Seq.whather中的函数而不使用;printf";?

F# 如何调用Seq.whather中的函数而不使用;printf";?,f#,sequences,seq,F#,Sequences,Seq,我是f#的新手,我试着编写一个程序来遍历给定目录中的所有文件,并为每个“.txt”类型的文件添加一个id号+“DONE” 我的节目: //const: [<Literal>] let notImportantString= "blahBlah" let mutable COUNT = 1.0 //funcs: //addNumber --> add the sequence number COUNT to each file. let addNumber (file : st

我是f#的新手,我试着编写一个程序来遍历给定目录中的所有文件,并为每个“.txt”类型的文件添加一个id号+“DONE”

我的节目:

//const:
[<Literal>]
let notImportantString= "blahBlah"
let mutable COUNT = 1.0

//funcs:
//addNumber --> add the sequence number COUNT to each file.
let addNumber (file : string)  =
 let mutable str = File.ReadAllText(file)
 printfn "%s" str//just for check
 let num = COUNT.ToString()
 let str4 = str + " " + num + "\n\n\n DONE"
 COUNT <- COUNT + 1.0
 let str2 =  File.WriteAllText(file,str4)
 file

//matchFunc --> check if is ".txt"
let matchFunc (file : string) =
 file.Contains(".txt") 

//allFiles --> go through all files of a given dir
let allFiles dir =

seq
    { for file in Directory.GetFiles(dir) do
        yield file  
           }

////////////////////////////

let dir = "D:\FSharpTesting"
let a = allFiles dir 
         |> Seq.filter(matchFunc) 
         |> Seq.map(addNumber)
printfn "%A" a
//常数:
[]
让notImportantString=“blahBlah”
设可变计数=1.0
//函数:
//addNumber-->将序列号计数添加到每个文件。
let addNumber(文件:字符串)=
让可变str=File.ReadAllText(文件)
printfn“%s”str//仅供检查
让num=COUNT.ToString()
让str4=str+“”+num+“\n\n完成”
计数检查是否为“.txt”
let matchFunc(文件:字符串)=
file.Contains(“.txt”)
//allFiles-->浏览给定目录的所有文件
让所有文件目录=
序号
{for Directory.GetFiles(dir)do中的文件
屈服文件
}
////////////////////////////
let dir=“D:\FSharpTesting”
设a=allFiles目录
|>序列过滤器(matchFunc)
|>序列图(地址编号)
printfn“%A”A
我的问题:

如果我不写最后一行(printfn“%A”A),文件将不会更改。(如果我写这一行,它将工作并更改文件) 当我使用debugger时,我发现当它到达行时,如果“let a=…”它继续到printfn行,那么它实际上不会计算“a”的值,而当它在那里“看到”a时,它会返回并计算“a”的答案。 为什么会这样?我如何在不打印的情况下“启动”功能

还有-有人能告诉我为什么我必须添加文件作为函数“addNumber”的返回类型吗? (我添加这个是因为它是如何工作的,但我真的不明白为什么……)

最后一个问题- 如果我将COUNT变量写在[]定义的行之后 它给出了一个错误,并表示常量不能“可变”,但如果在前面添加一行(这就是为什么我这样做的原因),它会“忘记”错误并工作。
为什么?如果你真的不能有一个可变常数,我怎么能做一个静态变量呢?

f#是自上而下计算的,但在你打印fn之前,你只会创建惰性值。因此,printfn实际上是第一个被执行的东西,它反过来执行代码的其余部分。我认为,如果你在Seq.map(addNumber)之后加上println,并在其上列出,也可以做同样的事情,这将强制进行评估。

Seq.map
旨在将一个值映射到另一个值,而不是通常对一个值进行变异
seq
表示一个延迟生成的序列,因此,正如Alex指出的,在序列被枚举之前不会发生任何事情。这可能更适合,但我会这样写:

Directory.enumerate文件(dir,“*.txt”)
|>Seq.iteri(乐趣i路径->
让text=File.ReadAllText(路径)
printfn“%s”文本
让text=sprintf“%s%d\n\n完成”文本(i+1)
File.WriteAllText(路径,文本))
Seq.map
需要返回类型,F#中的所有表达式都需要返回类型。如果一个函数执行一个操作,而不是计算一个值,它可以返回
unit
()
。关于
COUNT
,值不能是
可变的
[]
const
,在C#中)。这些恰恰相反。对于静态变量,使用模块作用域
let mutable
绑定:

module Counter =
  let mutable count = 1

open Counter
count <- count + 1

为了详细说明Alex的答案——F#序列被懒散地评估。这意味着序列中的每个元素都是“按需”生成的

这样做的好处是,您不会在不需要的元素上浪费计算时间和内存。不过,惰性评估确实需要一点时间来适应——特别是因为您不能假定执行顺序(或者执行根本不会发生)

您的问题有一个简单的解决方法:只需使用
Seq.iter
强制执行/计算序列,并将“ignore”函数传递给它,因为我们不关心序列返回的值

let a = allFiles dir 
     |> Seq.filter(matchFunc) 
     |> Seq.map(addNumber)
     |> Seq.iter ignore   // Forces the sequence to execute

这是惰性序列的一般行为。在中使用IEnumerable也有相同的值,比如说C#,seq是它的别名。 在伪代码中:

    var lazyseq = "abcdef".Select(a => print a); //does not do anything
    var b = lazyseq.ToArray(); //will evaluate the sequence
ToArray触发对序列的求值:

这说明了一个事实,即序列只是一个描述,并没有告诉您何时将枚举它:它由序列的使用者控制

let a = allFiles dir 
     |> Seq.filter(matchFunc) 
     |> Seq.map(addNumber)
     |> Seq.iter ignore   // Forces the sequence to execute

要进一步了解该主题,您可能需要了解:

最值得注意的是,序列不是缓存的(尽管您可以这样做)。您可以看到,描述和运行时行为之间的dintinguo可能具有重要的后果,因为重新计算序列本身会产生非常高的成本,并且如果每个值本身是线性的,则会引入二次操作数

如果不写入最后一行(printfn“%A”A),文件将不会更改

F#序列是惰性的。因此,为了强制求值,您可以执行一些不返回序列的操作。例如,您可以调用(有副作用,返回
()
),(返回一个
int
,它是序列的长度)或(返回一个列表,一个渴望的数据结构),等等

有人能告诉我为什么我必须添加
file:string
作为函数“addNumber”的返回类型吗

方法和属性访问与F#类型推断不兼容。类型检查器从左到右、从上到下工作。当你说
file.Contains
时,它不知道
Contains
成员应该是哪种类型。因此,您的类型注释对F#类型检查器是一个很好的提示

如果我将COUNT变量写在
[]
定义的行之后 它给出了一个错误,并表示常量不能“可变”

引述自:

可以使用Literal属性标记打算作为常量的值。此属性的作用是将值编译为常量

可变值可以在某个点更改其值
let isNebraskaCity_bad city =
    let cities =
        printfn "Creating cities Set"
        ["Bellevue"; "Omaha"; "Lincoln"; "Papillion"]
        |> Set.ofList

    cities.Contains(city)

let isNebraskaCity_good =
    let cities =
        printfn "Creating cities Set"
        ["Bellevue"; "Omaha"; "Lincoln"; "Papillion"]
        |> Set.ofList

    fun city -> cities.Contains(city)