2013-02-19 67 views
2

我是f#的新手,我試圖編寫一個程序,該程序應該通過給定目錄中的所有文件併爲每個類型爲「.txt」的文件添加一個id號碼+「完成」到文件。如何在沒有「printf」的情況下調用Seq.whatever中的函數?

我的程序:

//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 

我的問題:

TF我不寫的最後一行(printfn 「%A」 一個)的文件將不會改變(如果我不寫這條線。它的工作原理和更改文件) 當我使用調試器我發現它並沒有真正計算'a'的值,當它到達行時,如果「let a = ......」它繼續到printfn並且比它在「看到」'a'時返回並計算'a'的答案時更爲重要。 爲什麼它,我怎麼能「打開」功能,而不打印?

也有人可以告訴我爲什麼我必須添加文件作爲函數「addNumber」的返回類型? (我加了這一點,因爲它是如何工作的,但我真的不明白爲什麼....)

最後question- 如果我的[]定義 線後寫COUNT變量正確它提供了一個錯誤,並說一個常量不能「可變」,但如果添加(這是爲什麼我這樣做)另一行之前(如字符串)它「忘記」了錯誤和工作。 爲什麼?如果你真的不能有一個可變的常量,我該如何做一個靜態變量?

回答

9

如果我不寫最後一行(printfn「%A」a),文件不會改變。

F#序列是懶惰的。所以爲了強制評估,你可以執行一些不返回序列的操作。例如,可以調用Seq.iter(有副作用,返回()Seq.length(返回一個int這是序列的長度)或Seq.toList(返回一個列表,一個急切的數據結構),等等

有人可以告訴我爲什麼我必須添加file : string作爲函數「addNumber」的返回類型?

方法和屬性訪問對F#類型推斷不太好。類型檢查器從左到右,從上到下工作。當你說file.Contains時,它不知道這應該與Contains成員類型。因此,你的類型註釋是F#類型檢查器的一個很好的提示。

如果我寫了COUNT變量權[<Literal>]定義 線後,給出了一個錯誤,並說,一個恆定的不可能是「可變」

MSDN報價:

旨在爲常量的值可以使用Literal屬性進行標記。該屬性具有導致將值編譯爲常量的效果。

可變值可以在程序中的某個點更改其值;編譯器抱怨有一個很好的理由。您可以簡單地刪除[<Literal>]屬性。

+0

好的,我明白了! 非常感謝每一個! – nati 2013-02-19 19:56:50

1

f#從上到下進行評估,但是在創建printfn之前您只創建了惰性值。所以,printfn實際上是第一個被執行的東西,它依次執行其餘的代碼。我認爲如果你在Seq.map(addNumber)之後使用println,並且對它做強制評估的toList,你也可以做同樣的事情。

+0

非常感謝!我真的很高興得到答案! – nati 2013-02-19 20:15:02

3

Seq.map旨在將一個值映射到另一個值,而不是一般地改變值。 seq<_>代表一個懶惰生成的序列,因此,正如亞歷克斯指出的那樣,在序列被列舉之前什麼都不會發生。這可能是爲codereview更好的貼合,但這裏是我會怎麼寫:

Directory.EnumerateFiles(dir, "*.txt") 
    |> Seq.iteri (fun i path -> 
    let text = File.ReadAllText(path) 
    printfn "%s" text 
    let text = sprintf "%s %d\n\n\n DONE" text (i + 1) 
    File.WriteAllText(path, text)) 

Seq.map需要返回類型,因爲這樣做在F#中的所有表達式。如果一個函數執行一個動作,而不是計算一個值,它可以返回unit()。關於COUNT,值不能是mutable[<Literal>](C#中的const)。這些是精確的對立面。對於一個靜態變量,使用模塊範圍let mutable結合:

module Counter = 
    let mutable count = 1 

open Counter 
count <- count + 1 

但是你可以通過count與計數器變量作爲其私有實現的一部分功能避免全局可變數據。你可以用閉包來做到這一點:

let count = 
    let i = ref 0 
    fun() -> 
    incr i 
    !i 

let one = count() 
let two = count() 
+0

謝謝你的回答! – nati 2013-02-19 20:13:31

3

詳細闡述Alex的答案--F#序列被懶惰地評估。這意味着序列中的每個元素都是「按需」生成的。

這樣做的好處是,您不會浪費您不需要的元素的計算時間和內存。懶惰的評估確實需要一點時間才能習慣 - 特別是因爲你不能假定執行順序(或者甚至執行甚至會發生)。

您的問題有一個簡單的解決方法:只需使用Seq.iter來強制執行/評估序列,並將'忽略'函數傳遞給它,因爲我們不關心序列返回的值。

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

這可能需要'Seq.iter ignore',因爲'iter'需要一個函數。 – Daniel 2013-02-19 19:39:27

+0

謝謝!!!!!! – nati 2013-02-19 20:13:47

+0

@Daniel你說的沒錯。現在已經修復了。 – 2013-02-19 22:16:41

1

這是懶惰序列的一般行爲。你有相同的,比如說C#使用IEnumerable,其中seq是一個別名。 在僞代碼:

var lazyseq = "abcdef".Select(a => print a); //does not do anything 
    var b = lazyseq.ToArray(); //will evaluate the sequence 

ToArray的觸發序列的評價:

這說明了一個事實,即序列只是一個描述,不告訴你什麼時候會被列舉:此處於序列的消費者的控制下。


要走遠一點關於這個問題,你可能想看看this page from F# wikibook

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) 

最值得注意的是,序列不緩存(雖然可以讓他們如此)。然後你會發現描述和運行時行爲之間的dintinguo可能會產生重要的後果,因爲序列本身被重新計算,如果每個值本身是線性的,就會產生非常高的成本並引入二次操作數!

+0

謝謝everyOne !!!這是我第一次在這裏發佈一個問題,我沒有期望得到答案如此之快! :) 有沒有人知道我最後一個問題的答案? – nati 2013-02-19 19:47:56

+0

文字實際上將具有以字節碼編碼的值的二進制表示形式。它不是一個變量,而是一個真正的常量,比如PI。而名稱PI只是這個硬編碼值的一個捷徑。 – nicolas 2013-02-19 19:53:52

+0

@ user2088397:我在我的答案中添加了一些代碼來演示靜態變量。 – Daniel 2013-02-19 19:54:11

相關問題