Обработка текстового файла с вершин и возрастов


Следующий блок кода необходимо как можно быстрее, так как это могут быть вызваны много тысяч раз работать.

Я также сознаю, что мой стиль мышления склоняется к более процедурный стиль время от времени, и поэтому я не может быть в полной мере воспользоваться всеми преимуществами, что функциональное программирование не приносит.

Любые советы

  1. что делает код быстрее работать, и
  2. что делает его более функциональным

let qarray (tableName : string) (startAge : int) (startYear : int) =

let tableData = File.ReadAllLines tableName 

// find which row the vertex is in 
let vrow = Array.findIndex (fun (s:string) -> s.StartsWith vertexLiteral) tableData
let firstYear = int(tableData.[vrow].Split('\t').[1])

// filter out all the row prior to the column headers e.g. table description and comments
let filteredArray = Array.sub tableData (vrow+1) (tableData.Length-vrow-1)

// use the vertex info to read all lines beyond that, converting to doubles
let f (s:string) = s.Split('\t') |> Array.map double

let fullArray = Array.map f filteredArray

[| for i in 0 .. (120 - startAge - 1) -> fullArray.[startAge + i - 1].[System.Math.Min(startYear - firstYear + i + 1, fullArray.[0].Length)] |]


Комментарии
1 ответ

Вы должны отступ тело qarray функции, чтобы сделать его более очевидным, где она начинается и где ее конец (я имею в виду, я знаю, что это заканчивается в конце файла, в этом случае, но это не сразу видно на первый взгляд).

Вы также должны переименовать свои функции Ф во что-то более описательное.


let tableData = File.ReadAllLines tableName

Если вы используете .net версии 4.0, вы можете (и должны) использовать ReadLines , а не ReadAllLines как ReadAllLines считывает весь файл в память перед началом итерации, в то время как ReadLines загружает файл лениво что будет быстрее для больших файлов.

Отметим, что с ReadLines возвращает последовательность, а не массив, вы не можете открыть его с помощью индексов больше. Тем не менее, это хорошая вещь, как избавление от показателей приведет к более функциональным кодом.


// find which row the vertex is in 
let vrow = Array.findIndex (fun (s:string) -> s.StartsWith vertexLiteral) tableData
let firstYear = int(tableData.[vrow].Split('\t').[1])

// filter out all the row prior to the column headers e.g. table description and comments
let filteredArray = Array.sub tableData (vrow+1) (tableData.Length-vrow-1)

Как я сказал, Вы больше не можете использовать индекс подхода здесь. Идиоматические способ, чтобы избавиться от некоторых элементов в начале последовательности использовать skipWhile. Поскольку первый элемент мы хотим-это та, которая начинается с vertexLiteral, мы пропускаем элементы, пока они не начнут с vertexLiteral:

// Skip all rows up to the one the vertex is in
let relevantRows = tableData |> Seq.skipWhile (fun s -> not s.StartsWith vertexLiteral)

(Отметим, что |> написать таблицу сначала я позволил F# для определения типа ов и, следовательно, не нужен типа аннотации.)

Теперь мы можем использовать сл.голову , чтобы сделать первый ряд relevantRows (с вершины) и сл.пропустить 1 , чтобы получить остальные строки. Поэтому следующие строки станут:

let firstYear = int((Seq.head relevantRows).Split('\t').[1])

let filteredRows = Seq.skip 1 relevantRows


// use the vertex info to read all lines beyond that, converting to doubles
let f (s:string) = s.Split('\t') |> Array.map double

let fullArray = Array.map f filteredArray

Эти линии хорошо за исключением того, что теперь нужно использовать сл.карте вместо массива.карте (по крайней мере на первой линии, второй можно остановиться для проживания.карта как Сплит - прежнему возвращает массив, но нет никакого вреда в использовании СЛ , а), ф должен лучше имя и fullArray должен быть переименован, потому что это не массив больше.


[| for i in 0 .. (120 - startAge - 1) -> fullArray.[startAge + i - 1].[System.Math.Min(startYear - firstYear + i + 1, fullArray.[0].Length)] |]

ОК, Вот 120-это магическое число, которое вы не объяснили (что следует исправить путем документирования ее смысл), так что я не уверен, знаете ли вы, что таблица будет иметь ровно 120 элементов и у вас есть номер туда, чтобы избежать нарушения границ или же таблица может содержать более 120 элементов, и вы только хотите сделать первый 120. Я собираюсь предположить последнее дело.

Далее мне не понятно, почему вы используете fullArray.[0].Длина вместо fullArray.[startAge + я - 1]. Я собираюсь предположить, что все строки имеют одинаковую длину и вы выбрали 0 за startAge + я - 1 Для простоты.

Так что ты делаешь здесь в основном, чтобы пропустить первый startAge - 2 элементов, то индексации на каждого оставшегося элемента, используя минимальное ее длина и startYear - firstYear + я + 1 в качестве индекса. Это может быть достигнуто красиво без индекса на основе цикла с помощью сл.пропустить затем мапи (карта с индексом), такой:

fullSequence |> Seq.skip (startAge - 2) |>
Seq.mapi (fun i cols -> cols.[System.Math.Min(startYear - firstYear + i + 1, cols.Length)])

Поскольку это все-таки немного долго, возможно, стоило бы вынести удовольствие я смещ_по_столбцам -> суровостью.[Система.Математика.Мин(startYear - firstYear + я + 1, седла.Длина)] в имени функции.

6
ответ дан 20 марта 2011 в 08:03 Источник Поделиться