简体   繁体   English

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

[英]How to define the order of execution in F#?

In pure functional programming the order of execution doesn't matter and therefore is undetermined (ie it is up to the compiler). 在纯函数式编程中,执行顺序无关紧要,因此未确定(即由编译器决定)。 If you have side effects the order of execution does matter. 如果您有副作用,执行顺序很重要。 So how can it be defined in F#? 那么如何在F#中定义?

I have a function that recursively deletes all empty subfolders of a given path. 我有一个函数,递归删除给定路径的所有空子文件夹。 In addition it deletes some files contained in them if their names are in a given list. 此外,如果它们的名称在给定列表中,它将删除其中包含的一些文件。

The algorithm is easy: 算法很简单:

  1. Delete all files in the list. 删除列表中的所有文件。
  2. Do the recursive call for the subfolders. 对子文件夹进行递归调用。
  3. Delete the folder if it is empty. 如果文件夹为空,请删除该文件夹。 This must be the last step. 这必须是最后一步。

In addition the function returns the number of deleted elements as a tuple (numer of deleted folders, number of deleted files). 此外,该函数返回已删除元素的数量作为元组(已删除文件夹的数量,已删除文件的数量)。

Here is my code: 这是我的代码:

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)

I could figure out that DeleteEmptySubFolders is executed first, then DeleteFolder and DeleteFiles. 我可以弄清楚首先执行DeleteEmptySubFolders,然后执行DeleteFolder和DeleteFiles。 That is the order in which the functions appear in code. 这是函数在代码中出现的顺序。 But I don't think this is a rule of F#, it's just what the compiler decided. 但我不认为这是F#的规则,它正是编译器所决定的。 It could be any other order. 它可以是任何其他订单。

Of course I can swap the elements in the last line of my code. 当然,我可以在代码的最后一行交换元素。 The compiler would change the order of execution accordingly. 编译器会相应地更改执行顺序。 But since this is not a rule of the language it would just be luck. 但由于这不是语言的规则,它只是运气。

In another question on this topic I've read that values (ie functions without arguments) are initialized in the order in which they are declared. 在关于这个主题的另一个问题中 ,我已经读过,值(即没有参数的函数)按照它们被声明的顺序初始化。

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

accumulator (secondCall) (thirdCall, firstCall)

Now the calls happen to be in the right order. 现在呼叫恰好是正确的顺序。 But again: is this a rule of F# or just how the compiler works? 但同样:这是F#的规则还是编译器的工作原理? (if the functions are not used the compiler might decide to not initialize the values at all) (如果未使用这些函数,编译器可能决定根本不对这些值进行初始化)

How should I write the last line if I want to tell F# that the order of execution matters and when each call should be done? 如果我想告诉F#执行顺序是否重要以及何时应该完成每个调用,我该如何写最后一行? Is there a keyword or a special syntax to mark functions as not side effect free? 是否有关键字或特殊语法将函数标记为无副作用?

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

I think it would be best to just write your code as 我认为最好只编写代码

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

accumulator (secondCall) (thirdCall, firstCall)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM