我一直在研究使用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
答案 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 ]