如何在F#中定义执行顺序?

时间:2012-08-05 00:16:29

标签: f# functional-programming

在纯函数式编程中,执行顺序无关紧要,因此未确定(即由编译器决定)。如果您有副作用,执行顺序很重要。那怎样才能在F#中定义?

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

算法很简单:

  1. 删除列表中的所有文件。
  2. 对子文件夹进行递归调用。
  3. 删除文件夹(如果该文件夹为空)。这必须是最后一步。
  4. 此外,该函数返回已删除元素的数量作为元组(已删除文件夹的数量,已删除文件的数量)。

    这是我的代码:

    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#执行顺序是否重要以及何时应该进行每次调用,我该如何写最后一行?是否有关键字或特殊语法将函数标记为无副作用?

2 个答案:

答案 0 :(得分:7)

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

答案 1 :(得分:1)

我认为最好将代码编写为

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

accumulator (secondCall) (thirdCall, firstCall)