2015-09-09 2 views
1

Я новичок в F #, поэтому увлекаюсь попытками приблизиться к проблемам более функциональным способом (а не с императивным мышлением, к которому я привык).Функциональный подход к инициализирующим классам

Для этой проблемы, я читаю данные из Excel, который содержит ~ 6000 строк данных с несколькими полями (возраст, пол, пол и т. Д.).

Я использую класс для хранения этих точек данных в - извлечение ниже:

type datastore(array:obj[,], row)= 
    let id=array.[row,1]:?> string; 
    let gender=array.[row,2] :?> string; 
    let sex = array.[row,3] :?> string; 
    //...continues for a number of other cases 

В настоящее время я Инициирование эти классы со следующим кодом, который прекрасно работает, но является императивом подход:

let DataPoints = 
    let DataInput = //function to read data in from Excel 
    [for i in DataInput.GetLowerBound(0) .. DataInput.GetUpperBound(0) -> 
     new datastore(DataInput,i)] 

Мой вопрос: как я могу сделать это более функционально?

Я пробовал следующее (что не работает: данная ошибка просто «остановлена ​​из-за ошибки»).

let num = DataInput.GetUpperBound(0) 
let MPs3 = Array.init num (fun x -> new datastore(DataInput,x)) 

Я также рассмотрел следующие ссылки, которые, похоже, не помогли полностью.

Array initialization in F#

How to define a record field as an array in f#?

+0

Использование ExcelProvider: http://fsprojects.github.io/ExcelProvider/ –

+0

Спасибо за ссылку. У вас было общее руководство по моей конкретной проблеме, если я продолжу свой нынешний подход? – Pash101

+1

Ваш код кажется прекрасным. Почему вы думаете, что это необходимо? –

ответ

3

Функциональное программирование - одна из тех вещей, каждая из которых имеет собственное личное определение. Если вы спросите меня, два качества, которые выделяются являются:

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

Итак, если у меня возникла такая проблема, как у вас, я бы сначала определил строго типизированное представление данных.Записи лучше подходит, чем классы для хранения «тупые» данные, так это то, что я хотел бы использовать:

type DataPoint = 
    { 
     Id:  string 
     Gender: GenderType 
     Sex: SexType 
    } 
    static member FromRow(row: obj []) = 
     { 
      Id  = row.[0] :?> string 
      Gender = GenderType.Parse <| (row.[1] :?> string) 
      Sex = SexType.Parse <| (row.[2] :?> string) 
     } 

Для правильного представления, Gender и Sex действительно должны подвергаться дискриминации союзы с Parse функций, а не строки, но я 'll оставить их как упражнение для читателя;)

Как только у вас есть способ превратить строку в массив в DataPoint, вам действительно нужно получить строки. Для этого давайте сократим 2d-массив, который вы получаете из Excel, в последовательность строк. Там нет стандартной функции, чтобы сделать это, но это должно выглядеть примерно так:

module Array2D = 
    let intoRows (arr: obj[,]) : seq<obj []> = 
     seq { for x in 0 .. Array2D.length1 arr - 1 do 
        yield [| for y in 0 .. Array2D.length2 arr - 1 -> arr.[x, y] |]} 

С этим, у нас есть все, что нужно, так что теперь просто положить их вместе в трубопровод:

let dataPoints = 
    let input = //function to read data in from Excel 
    input 
    |> Array2D.intoRows 
    |> Seq.map DataPoint.FromRow 
    |> Array.ofSeq 

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

+0

Пол и пол слишком сложны для правильно обрабатывать дискриминированный союз. В большинстве случаев лучше всего не заниматься с ними вообще! – dfeuer

2

Я бы не поставить его в качестве "менее или более функциональным". DataPoints в вашем втором фрагменте из datastore значения, инициализированные F # list comprehension механизм.

Если вы хотите DataPoints быть вместо этого массива из datastore значений инициализируются с помощью стандартной библиотечной функции Array.init код корреспондента может выглядеть

let DataPoints = 
    let (il,ih) = (DataInput.GetLowerBound(0),DataInput.GetUpperBound(0)) in 
    Array.init (ih - il + 1) (fun x -> new datastore(DataInput, x + il)) 

для того, чтобы принять данным Array.init элемент индекса обработки от 0 до number of elements - 1 требуется от DataInput.GetLowerBound(0) до DataInput.GetUpperBound(0) - DataInput.GetLowerBound(0).

Но оба подхода являются формами функционального программирования для генерации последовательностей элементов.

+0

Это то, что я сделал бы, если бы захотел использовать 'Array.init', но стоит отметить, что в этом случае понимание списка выглядит намного проще :-) –

Смежные вопросы