我开始学f#了。我发现很难改变我对OO编程的看法。我想知道f#开发人员如何编写以下内容:
"传统"c#
public List<List<string>> Parse(string csvData){
var matrix = new List<List<string>>();
foreach(var line in csvData.Split(Environment.NewLine.ToArray(), StringSplitOptions.None)){
var currentLine = new List<string>();
foreach(var cell in line.Split(','){
currentLine.Add(cell);
}
matrix.Add(currentLine);
}
return matrix;
}
"功能性"c#
public List<List<string>> Parse(string csvData){
return csvData.Split(Environment.NewLine.ToArray(), StringSplitOptions.None).Select(x => x.Split(',').ToList()).ToList();
}
问题是:下面的代码是正确的吗? f# let Parse(csvData:string) =
csvData.Split(Environment.NewLine.ToArray(), StringSplitOptions.None).ToList()
|> Seq.map(fun x -> x.Split(',').ToList())
我看你的翻译不错。在c#中使用扩展方法(例如foo.Select(...)
)大致相当于在List
, Seq
或Array
模块中使用管道和f#函数,具体取决于您使用的集合类型(例如foo |> Seq.map (...)
)。
使用来自f#的LINQ扩展方法并将它们与f#结构混合使用是完全可以的,但我只会在没有相应的f#函数时这样做,因此我可能会避免示例中的ToArray()
和ToList()
并编写:
open System
let Parse(csvData:string) =
// You can pass the result of `Split` (an array) directly to `Seq.map`
csvData.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.None)
// If you do not want to get sequence of arrays (but a sequence of F# lists)
// you can convert the result using `Seq.toList`, but I think working with
// arrays will be actually easier when processing CSV data
|> Seq.map(fun x -> x.Split(',') |> Seq.toList)
在您的解决方案中有一些东西不是很f#:
- 可变
List
在f#中被命名为ResizeArray,它们没有很好的支持作为内置集合,包括Array, List, Seq。 你不需要经常在f#中使用LINQ, Array, List和Seq模块绰绰有余。 - dot(.)语法不能很好地处理管道操作符。
将返回类型更改为string [] []
,您可以使用辅助函数编写更简洁的代码:
let split (delim: string) (str: string) =
str.Split([|delim|], StringSplitOptions.RemoveEmptyEntries)
let parse (csvData: string) =
csvData
|> split Environment.NewLine
|> Array.map (split ",")
本页上的各种解决方案之间存在细微差异。例如,你的c#解决方案和pad的解决方案是渴望的,但你的f#版本和Tomas的是懒惰的。你的和pad在Environment.NewLine
上分裂,但Tomas在NewLine
中的任何字符上分裂(我猜你想要后者,因为你调用了接受StringSplitOptions
的过载)。也许这些很重要;也许不是。
无论如何,这里有一个不同的pad的解决方案。
let split (delims: char[]) (str: string) =
str.Split(delims, StringSplitOptions.RemoveEmptyEntries)
let parse csvData =
let nl = Environment.NewLine.ToCharArray()
[ for x in split nl csvData -> split [|','|] x ]