使用f#模仿linux touch命令,需要帮助实现

时间:2019-09-02 17:51:30

标签: f# touch f#-interactive

我正在尝试编写一个f#程序,该程序模仿linux中的touch命令,它会检查文件路径是否存在(如果确实更新了时间戳记,是否不存在创建的文件)。我如何正确实现呢?

我尝试使用.net函数,我认为它基本上可以满足我的需要。我正在使用file.exists,file.create和file.setlastaccesstime

//通过http://fsharp.org

了解有关F#的更多信息
open System.IO

type Filepath = string


   let touch = 

    let checkExists filePath =
    if File.Exists filePath
    then Result.Ok (filePath:Filepath)
    else Result.Error "File does not exist"
     let path = Console.ReadLine()

     let update =
     path = Console.ReadLine()
     if File.Exists(path)
     File.Create(path)
     else File.SetLastAccessTime(path, DateTime.Now)

[<EntryPoint>]
let main argv =
    printfn "Touch command for windows"
    -> touch()
    -> update()

    0 // return an integer exit code```

1 个答案:

答案 0 :(得分:4)

您的代码有几个问题。我会尝试一个接一个地解决它们。

首先,您问题中代码的缩进完全搞砸了。如果这是从您的代码直接复制并粘贴,则我们要做的第一件事就是修复缩进:在F#中,缩进很重要,并确定代码块。 (如果您的问题的缩进被弄乱了,因为您在Stack Overflow问题编辑器中摆弄了缩进,而您的实际代码缩进就可以了,那么考虑一下F#缩进的工作原理。

在F#中,这两个代码块做一些不同的事情:

if value = 5 then
    printfn "Five"
printfn "Value: %d" value

这将始终打印“值:3”或“值:27”或其他值,并且如果值正好为5,还将打印单词“五”。但是下一个代码块还会执行其他操作:

if value = 5 then
    printfn "Five"
    printfn "Value: %d" value

这将完全不打印任何内容,或者将打印单词“ Five”(五),然后在下一行显示“ Value:5”。这是因为第二个printfn函数调用的缩进程度与第一个相同,这意味着它是if块的一部分。

此外,在let语句后必须在同一行上加上一个值或在一个缩进的块之后:

let value = 5

let otherValue =
    if value < 5 then
        5
    else
        3

执行此操作后,otherValue的值为3。此外,let otherValue =下的代码块将仅一次执行。这是因为otherValue不是函数,而是一个值。如果您希望otherValue是一个函数,则需要为其提供参数:

let calculateValue inputValue =
    if inputValue < 5 then
        5
    else
        3

现在您可以像下面这样调用该函数了:

let value = 5
let otherValue = calculateValue value
// Now otherValue is equal to 3

这使我们想到了代码的第二个问题,即您显然打算将touchupdate用作函数,但是没有给它们提供参数。这意味着它们是,仅执行一次。您需要将let touch =转换为let touch () =,以使touch成为函数而不是值,而对update进行修改。

您遇到的另一个问题更多是设计问题:您的update函数正在做两件事。它正在从控制台读取一个值,并根据该值执行操作。更好的设计是让每个函数只做一件事:让您的update函数以path作为参数,然后在其他地方您可以拥有从控制台读取path的代码,呼叫update path。这使编写测试update函数的单元测试变得更加容易,因为您不必以某种方式弄清楚如何将单元测试连接到stdin,您只需让您的测试调用update功能。

古兰在评论中指出的另一个问题(谢谢!)是您的update函数具有倒退的逻辑。您写了“如果文件存在,创建文件,否则设置上次访问时间”。情况应该相反。我在此答案的第一版中错过了这一点,因此我更新了下面的固定代码,以更正if...then...elseupdate块的顺序。

我看到的另一个问题是:您应该执行的touch函数是什么(当前是一个值,但您显然打算将其作为一个函数)? let touch =块中唯一的一件事就是其他函数定义。 touch代码块从不实际调用其定义的函数。这意味着它没有用:通过在touch代码块的内部 中使用这些函数定义,您可以将它们隐藏在touch代码块之外的所有代码中,而在{{1 }}代码块从不调用它们。可以访问该模块的任何代码都可以看到在模块顶层定义的功能。在代码块内部定义的函数(和变量)仅在该代码块的范围内可见。这对于封装外界不应该看到的东西很有用,例如在以下示例中:

touch

在这里,let counter() = let mutable value = 0 let update() = value <- value + 1 value update 内的value变量对于世界其他地方是不可访问的。另外,请注意我是如何在counter()代码块的最后一行写上没有括号的update的:这意味着名为let counter() =的函数将是{{1 }}。换句话说,当您调用update时,您将得到一个函数,每次调用它时,将返回一个新值,该值是前一个值加1。但是两个不同的计数器彼此分开:

counter()

这将打印“ A1:1”,“ A2:2”,“ B1:1”,然后打印“ B2:2”。现在作为练习,如果在上面的代码中将counter()替换为let a = counter() let b = counter() let a1 = a() let b1 = b() let a2 = a() let b2 = b() printfn "A1: %d" a1 printfn "A2: %d" a2 printfn "B1: %d" b1 printfn "B2: %d" b2 会发生什么? (我还必须将let counter() =更改为let counter =,对于let a = counter()也是一样)。尝试猜测会发生什么,然后尝试一下,看看您是否正确。 (提示:与函数不同,值仅在一次上执行)。

最后一件事:let a = counter函数中的箭头不正确。这不是let b = ...运算符的作用。如果需要连续调用多个函数,只需编写一个接一个的函数调用即可。

好的,这就是我现在所能做的,可以帮助您解决代码问题。问题更多(例如,从main获取路径而不是从->获取路径),但是这些问题可以等到您掌握了更多F#经验后再进行。这是您的代码,其中只有我到目前为止提到的修复程序。哦,还有一个解决方法:由于我无法告知您打算在argv函数中输入什么内容,因此我将删除Console.ReadLine()行。因此,这是您的代码,虽然有些固定,但仍需要更多修正:

let touch =