有用的F#脚本

时间:2010-02-16 02:37:33

标签: f# scripting

我一直在研究使用F#进行开发,并且发现(对于我的情况)构建脚本以帮助我简化一些复杂的任务,这是我可以从中获得价值的(目前)。

我最常见的复杂任务是连接许多任务的文件(主要是SQL相关的)。

我经常这样做,每当我尝试改进我的F#脚本来执行此操作时。

这是我迄今为止的最大努力:

open System.IO

let path = "C:\\FSharp\\"
let pattern = "*.txt"
let out_path = path + "concat.out"

File.Delete(out_path)
Directory.GetFiles(path, pattern)
 |> Array.collect (fun file -> File.ReadAllLines(file))
 |> (fun content -> File.WriteAllLines(out_path, content) )

我确信其他人的脚本会使他们有时更复杂/更无聊的任务更容易。 您曾经使用过哪些F#脚本或者您认为F#脚本的其他用途有用吗?

我发现改善F#的最佳方法是浏览其他脚本以获取有关如何解决特定情况的想法。希望这个问题将来会帮助我和其他人。 :)

我找到了一篇关于生成可能感兴趣的F#脚本的文章: http://blogs.msdn.com/chrsmith/archive/2008/09/12/scripting-in-f.aspx

4 个答案:

答案 0 :(得分:6)

当我需要快速预处理某些数据或在各种格式之间转换数据时,我以类似的方式使用F#。 F#具有很大的优势,您可以创建更高阶的函数来执行各种类似的任务。

例如,我需要从SQL数据库加载一些数据并生成加载数据的Matlab脚本文件。我需要为几个不同的SQL查询执行此操作,因此我编写了这两个函数:

// Runs the specified query 'str' and reads results using 'f'
let query str f = seq { 
  let conn = new SqlConnection("<conn.str>");
  let cmd = new SqlCommand(str, conn)
  conn.Open()
  use rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection)
  while rdr.Read() do yield f(rdr) } 

// Simple function to save all data to the specified file
let save file data = 
  File.WriteAllLines(@"C:\...\" + file, data |> Array.ofSeq)

现在我可以轻松编写特定的调用来读取我需要的数据,将它们转换为F#数据类型,进行一些预处理(如果需要)并将输出打印到文件中。例如,对于处理公司我有类似的东西:

let comps = 
  query "SELECT [ID], [Name] FROM [Companies] ORDER BY [ID]" 
        (fun rdr ->  rdr.GetString(1) )
let cdata = 
  seq { yield "function res = companies()"
        yield "  res = {"
        for name in comps do yield sprintf "   %s" name
        yield "  };"
        yield "end" }
save "companies.m" cdata

将输出生成为字符串序列也非常简洁,尽管您可以使用StringBuilder编写更高效的计算构建器。

我在第13章的functional programming book中描述了以交互方式使用F#的另一个例子(你可以get the source code here)。它连接到世界银行数据库(其中包含大量有关各国的信息),提取一些数据,探索数据结构,将其转换为F#数据类型并计算一些结果(并将其可视化)。我认为这是(很多)任务之一,可以在F#中很好地完成。

答案 1 :(得分:2)

有时候,如果我想要一个XML结构的简介(或者有一个递归列表用于搜索等其他形式),我可以使用以下脚本在XML中打印出一个选项卡式节点列表:

open System
open System.Xml

let path = "C:\\XML\\"
let xml_file = path + "Test.xml"

let print_element level (node:XmlNode) = [ for tabs in 1..level -> "   " ] @ [node.Name]
                                            |> (String.concat "" >> printfn "%s")

let rec print_tree level (element:XmlNode) = 
        element 
        |> print_element level
        |> (fun _ -> [ for child in element.ChildNodes -> print_tree (level+1) child ])
        |> ignore

new XmlDocument()
 |> (fun doc -> doc.Load(xml_file); doc)
 |> (fun doc -> print_tree 0 doc.DocumentElement)

我确信它可以优化/减少,并鼓励其他人对此代码进行改进。 :)

答案 2 :(得分:2)

(有关替代摘录,请参阅下面的答案。) 此代码段使用XSLT转换XML。我不确定在F#中最好的方式使用XslCompiledTransform和XmlDocument对象,但它似乎有效。我相信有更好的方法,并乐于听到它们。

(* Transforms an XML document given an XSLT. *)

open System.IO
open System.Text
open System.Xml
open System.Xml.Xsl

let path = "C:\\XSL\\"

let file_xml = path + "test.xml"
let file_xsl = path + "xml-to-xhtml.xsl"

(* Compile XSL file to allow transforms *)
let compile_xsl (xsl_file:string) = new XslCompiledTransform() |> (fun compiled -> compiled.Load(xsl_file); compiled)
let load_xml (xml_file:string) = new XmlDocument() |> (fun doc -> doc.Load(xml_file); doc)

(* Transform an Xml document given an XSL (compiled *)
let transform (xsl_file:string) (xml_file:string) = 
      new MemoryStream()
        |> (fun mem -> (compile_xsl xsl_file).Transform((load_xml xml_file), new XmlTextWriter(mem, Encoding.UTF8)); mem)
        |> (fun mem -> mem.Position <- (int64)0; mem.ToArray())

(* Return an Xml fo document that has been transformed *)
transform file_xsl file_xml
    |> (fun bytes -> File.WriteAllBytes(path + "out.html", bytes))

答案 3 :(得分:0)

在阐明使用现有.net类编写F#代码的方法之后,出现了以下有用的代码,用于在给定xsl文档的情况下转换xml文档。该函数还允许您创建自定义函数以使用特定的xsl文档转换xml文档(请参阅示例):

let transform = 
    (fun xsl ->
        let xsl_doc = new XslCompiledTransform()
        xsl_doc.Load(string xsl)

        (fun xml -> 
            let doc = new XmlDocument() 
            doc.Load(string xml)
            let mem = new MemoryStream()
            xsl_doc.Transform(doc.CreateNavigator(), null, mem)
            mem
        )
    )

这允许您以这种方式转换文档:

let result = transform "report.xml" "report.xsl"

或者你可以创建另一个可以多次使用的功能:

let transform_report "report.xsl"

let reports = [| "report1.xml"; "report2.xml" |]
let results = [ for report in reports do transform_report report ]