2012-08-05 21 views
3

在纯函数式编程中,执行顺序无关紧要,因此不确定(即由编译器决定)。如果您有副作用,执行顺序就很重要。那么如何在F#中定义呢?如何定义F#中的执行顺序?

我有一个递归删除给定路径的所有空子文件夹的函数。另外,如果它们的名字在给定列表中,它将删除它们中包含的一些文件。

的算法很简单:

  1. 删除列表中的所有文件。
  2. 对子文件夹执行递归调用。
  3. 如果文件夹为空,请删除该文件夹。这必须是最后一步。

此外,该函数还将删除元素的数量作为元组返回(已删除文件夹的数量,已删除文件的数量)。

这里是我的代码:

let rec DeleteEmptyFolders path filenames = 
    // Deletes a file or folder using the given function. 
    // Returns 1 if the file could be deleted, otherwise 0. 
    let Delete delete name = 
     try 
      delete name; 
      1 
     with 
      | _ -> 0 

    // The function result (number of deleted folders and files). 
    let deletedFolders (a, _) = a 
    let deletedFiles (_, a) = a 

    let accumulator a b = 
     (deletedFolders a + deletedFolders b, 
      deletedFiles a + deletedFiles b) 

    // Deletes the given files and returns the number of deleted elements. 
    let DeleteFiles folder names = 
     names 
     |> Seq.map (fun n -> Path.Combine (folder, n)) 
     |> Seq.map (fun n -> Delete File.Delete n) 
     |> Seq.reduce (+) 

    // Deletes the folder if it is empty 
    // (Directory.Delete will fail if it is not empty). 
    let DeleteFolder folder = Delete Directory.Delete folder 

    // The recursive call 
    let DeleteEmptySubFolders folder files = 
     Directory.EnumerateDirectories folder 
     |> Seq.map (fun p -> DeleteEmptyFolders p files) 
     |> Seq.reduce (accumulator) 

    // Three functions are executed: DeleteEmptySubFolders, DeleteFolder and DeleteFiles 
    // But it has to be done in the correct order: DeleteFolder must be executed last. 
    accumulator (DeleteEmptySubFolders path filenames) (DeleteFolder path, DeleteFiles path filenames) 

是DeleteEmptySubFolders首先被执行,然后和的DeleteFolder我DeleteFiles能弄清楚。 这是函数在代码中出现的顺序。但我不认为这是F#的规则,这正是编译器决定的。它可以是其他任何顺序。

当然,我可以在我的代码的最后一行交换元素。编译器会相应地改变执行顺序。但是,由于这不是语言的规则,它只是运气。

another question关于这个话题我读过的值(即没有参数的函数)是按声明顺序初始化的。

let firstCall = DeleteFiles path filenames 
let secondCall = DeleteEmptySubFolders path filenames 
let thirdCall = DeleteFolder path 

accumulator (secondCall) (thirdCall, firstCall) 

现在呼叫恰巧按正确的顺序。但是,这又是一个F#的规则还是编译器的工作原理? (如果没有使用这些函数,编译器可能决定根本不初始化这些值)

如果我想告诉F#执行顺序很重要以及每次调用何时应该完成,我应该如何编写最后一行?是否有一个关键字或特殊的语法来标记功能不是副作用?

+1

大多数纯函数式语言会要求您使用的代码用特殊的语法,而不需要最终调用网站标注实际的副作用添加该注释。 – Guvante 2012-08-06 16:16:49

回答

7

F#不是一种纯粹的函数式编程语言:函数和值将从上到下,从左到右计算。

+0

执行顺序(从上到下,从左到右)是该语言的规则还是编译器的设计? – pescolino 2012-08-05 00:57:54

+2

@pescolino:前者。 – ildjarn 2012-08-05 01:13:12

1

我认为这将是最好只写你的代码

let firstCall = DeleteFiles path filenames 
let secondCall = DeleteEmptySubFolders path filenames 
let thirdCall = DeleteFolder path 

accumulator (secondCall) (thirdCall, firstCall)