【问题标题】:Reading from a text file and sorting从文本文件中读取并排序
【发布时间】:2017-01-13 08:28:56
【问题描述】:

我已设法读取包含逐行随机数的文本文件。当我使用printfn "%A" lines 输出行时,我得到seq ["45"; "5435" "34"; ... ],所以我假设行必须是数据类型列表。

open System
let readLines filePath = System.IO.File.ReadLines(filePath);;
let lines = readLines @"C:\Users\Dan\Desktop\unsorted.txt"

我现在尝试按从低到高对列表进行排序,但它没有 .sortBy() 方法。任何人都可以告诉我如何手动执行此操作?我试过把它变成一个数组来排序,但它不起作用。

let array = [||]
let counter = 0
for i in lines do
 array.[counter] = i
 counter +1
Console.ReadKey <| ignore

提前致谢。

【问题讨论】:

  • 这是一个序列,而不是一个列表。试试Seq.sortBy
  • 那么,它看起来像 let sorted = lines.Seq.sortBy 吗? @rmunn
  • 没有。 Seq.sortBy 是一个函数,而不是一个方法。应该是let sorted = Seq.sortBy int lines,或let sorted = lines |&gt; Seq.sortBy int
  • 需要意识到的一点是,序列是惰性的,只有在需要它们的内容时才会生成它们的内容。然后您将只能获取一次 seq 的内容,否则您将收到“错误:无法从关闭的 TextReader 中读取”。如果您来自 C# 背景,请记住 F# 序列与 C# 中的 IEnumerable 完全相同,并且您基本上会做对。
  • 请注意,other 序列可以很好地重复生成它们的值。这只是您从System.IO.File.ReadLines 获得的序列(IEnumerable),您将无法重复。如果您需要重复遍历其值,请将其存储在列表或数组中,通过let storedLines = lines |&gt; List.ofSeq(或lines |&gt; Array.ofSeq)。你想要一个列表还是一个数组将取决于:你想要一个单链表(F#list 类型是一个链表),具有 O(1) 头访问和附加但 O(N) 随机访问,或者一个 O(1) 随机访问但追加速度慢的数组?

标签: f#


【解决方案1】:

如果所有的行都是整数,你可以使用Seq.sortBy int,像这样:

open System
let readLines filePath = System.IO.File.ReadLines(filePath)
let lines = readLines @"C:\Users\Dan\Desktop\unsorted.txt"
let sorted = lines |> Seq.sortBy int

如果某些行可能不是有效的整数,那么您需要执行解析和验证步骤。例如:

let tryParseInt s =
    match System.Int32.TryParse s with
    | true, n -> Some n
    | false, _ -> None
let readLines filePath = System.IO.File.ReadLines(filePath)
let lines = readLines @"C:\Users\Dan\Desktop\unsorted.txt"
let sorted = lines |> Seq.choose tryParseInt |> Seq.sort

注意我刚才写的tryParseInt函数返回的是int值,所以我用Seq.sort而不是Seq.sortBy int,这个函数链的输出将是一个int序列而不是一个序列的字符串。如果你真的想要一个字符串序列,但只有可以解析为整数的字符串,你可以这样做:

let tryParseInt s =
    match System.Int32.TryParse s with
    | true, _ -> Some s
    | false, _ -> None
let readLines filePath = System.IO.File.ReadLines(filePath)
let lines = readLines @"C:\Users\Dan\Desktop\unsorted.txt"
let sorted = lines |> Seq.choose tryParseInt |> Seq.sortBy int

注意我是如何从这个版本的tryParseInt 返回s,以便Seq.choose 保留字符串(但丢弃任何未能通过System.Int32.TryParse 验证的字符串)。还有更多的可能性,但这应该足以让您开始。

【讨论】:

  • 尽管我很喜欢细节,let sorted = Seq.sortBy int lines 工作得很好 :) 但是 +1 的信息,我也刚刚做了一个 for x in sorted 并输出了每个数字 :)
【解决方案2】:

所有 cmets 都是有效的,但我更关心您的命令式循环。

下面是一个例子:

读取所有行:

open System.IO

let file = @"c:\tmp\sort.csv"
let lines = File.ReadAllLines(file)

要对行进行排序: let sorted = Seq.sort lines

sorted |&gt; Seq.length // to get the number of lines
sorted |&gt; Seq.map (fun x -&gt; x.Length) // to iterate over all lines and get the length of each line

您还可以使用列表解析语法:
[for l in sorted -&gt; l.ToUpper()]

Seq 适用于所有类型的集合,但您可以将其替换为 Array(可变)或 List(F# List)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-15
    • 1970-01-01
    • 1970-01-01
    • 2018-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多