Pipe Array.reduce FileInfo [] [] to FileInfo []

时间:2016-10-08 00:33:58

标签: f#

exception NoDirectory of string

let dir = DirectoryInfo "c:\\"

let dirExists (dir: DirectoryInfo): bool = dir.Exists


let rec getFiles (dir: DirectoryInfo) : FileInfo[] = 
    let subs = 
            match dirExists dir with
                | true -> dir.GetDirectories ""
                | false -> raise (NoDirectory("error"))
    subs
      |> Array.map (fun (dir: DirectoryInfo) -> getFiles dir)
      |> Array.reduce (fun acc elem -> acc.append elem) List.empty 

因此,我尝试将FileInfo[][]简化为FileInfo[]或更准确地将FileInfo[][]缩减为List<FileInfo>

我认为因为管道只适用于一元函数,所以这意味着函数可以调整Array.reduce,但我有点卡在那里,例如:

let unaryReduce (fileInfoArray: FileInfo[]) = Array.reduce (fun acc elem -> acc.append elem) List.empty

我是对的吗?靠近......很远......

修改

经过一些修改后我得到了这个:

let dir = DirectoryInfo "c:\\"

let dirExists (dir: DirectoryInfo): bool = dir.Exists

let getFiles (dir: DirectoryInfo) : FileInfo[] = dir.GetFiles ""

let rec recursiveGetFiles (dir: DirectoryInfo) : FileInfo[] = 
    let subs = 
            match dirExists dir with
                | true -> dir.GetDirectories ""
                | false -> raise (NoDirectory("error"))
    subs
      |> Array.collect (fun (dir: DirectoryInfo) -> recursiveGetFiles dir)
      |> Array.concat<FileInfo[]> getFiles <| dir

我昨天才开始看F#,我想尝试使用烟斗。我得到的错误是在最后一行:

  

此表达式应具有类型

     

FileInfo [] - &gt; &#39; a - &gt; FileInfo []
  但这里有类型

     

FileInfo [] []

这告诉我,collect并未正确使用以展平数组......

3 个答案:

答案 0 :(得分:2)

如果你想使用流水线技术,这样的事情应该有效:

exception NoDirectory of string

// DirectoryInfo -> bool
let dirExists (dir: DirectoryInfo) = dir.Exists

// DirectoryInfo -> FileInfo []
let getFiles (dir: DirectoryInfo) = dir.GetFiles "."

// DirectoryInfo -> FileInfo []
let rec recursiveGetFiles dir = 
    match dirExists dir with
    | true -> dir.GetDirectories "."
    | false -> raise (NoDirectory "error")
    |> Array.collect recursiveGetFiles
    |> Array.append (getFiles dir)

我不建议抛出此处所示的异常,但我保留了这一部分,以使其尽可能接近OP。但是,一旦您熟悉了F#和函数式编程的基础知识,就应该了解使用Either monad的函数错误处理。一个好的起点是:http://fsharpforfunandprofit.com/rop

答案 1 :(得分:1)

您从编辑中获得的错误来自后面管道的优先级(&lt; |)a |> f <| b被视为(a |> f) <| b当您想要的是a |> (f <| b)时,a |> (f b)只是let getAllFiles (dir: DirectoryInfo) = let rec aux dir = [ for file in getFiles dir -> file for subDir in getDirs dir do yield! aux subDir ] if dirExists dir then aux dir else raise <| NoDirectory "error" 1}} (如Mark Seemann's answer中所示)

那说你可以用这种方式写一些东西(IMO):

list

这样我们将递归部分与检查部分分开 递归部分更简单,更易读,最初返回getDirs而不是数组(我添加了if
检查部分由true/false完成,因为getDirs的匹配不会在此处添加值 并且仅针对初始参数完成,因为let rec getAllFiles (dir: DirectoryInfo) = [ if dirExists dir then for file in getFiles dir -> file for subDir in getDirs dir do yield! getAllFiles subDir ] 很少有机会返回不存在的目录*

*即使有人在错误的时间删除了一个,但在这种情况下你可以重写它

{{1}}

这样你甚至不需要提出错误,只需获得一个不存在的目录的空列表。

答案 2 :(得分:0)

如果你需要seq,你也可以使用EnumarateDirectories,虽然使用IO我会小心使用延迟集合。这将返回我在项目(子)目录中的所有56个文件:

open System.IO
let dir = DirectoryInfo __SOURCE_DIRECTORY__
dir.GetDirectories("*",SearchOption.AllDirectories) |> Array.collect (fun x -> x.GetFileSystemInfos()) 

如果用Array.collect替换Array.map,则顶级集合将是目录的集合(正好是我所拥有的5)。