Pytanie Dodatkowe konstruktory w F #


Próbuję utworzyć dodatkowy konstruktor w F #, który wykonuje dodatkową pracę (czyli odczytuje podstawowy plik csv) w następujący sposób:

type Sheet () =
  let rows = new ResizeArray<ResizeArray<String>>()
  let mutable width = 0

  new(fileName) as this = 
    Sheet() 
    then
      let lines = System.IO.File.ReadLines fileName
      for line in lines do
        let cells = line.Split ','
        rows.Add(new ResizeArray<String> (cells)) //line 16
        if cells.Length > width then width <- cells.Length

ale dostaję następujące błędy:

Error   1   The namespace or module 'rows' is not defined   C:\Users\ga1009\Documents\PhD\cpp\pmi\fsharp\pmi\Csv.fs 16
Error   2   The value or constructor 'width' is not defined C:\Users\ga1009\Documents\PhD\cpp\pmi\fsharp\pmi\Csv.fs 17
Error   3   The value or constructor 'width' is not defined C:\Users\ga1009\Documents\PhD\cpp\pmi\fsharp\pmi\Csv.fs 17

Co ja robię źle?


9
2017-08-21 14:00


pochodzenie




Odpowiedzi:


Jak zauważył Daniel, projekt F # polega na tym, że masz jeden główny konstruktor, który zazwyczaj bierze wszystkie argumenty potrzebne klasie i wykonuje inicjalizację. Inne konstruktory mogą dostarczać wartości domyślne dla argumentów lub obliczać je na podstawie innych informacji.

W twoim przypadku myślę, że najlepszy byłby projekt rows jako argument konstruktora. Następnie możesz dodać dwa dodatkowe konstruktory (jeden, który ładuje plik i inny, który dostarcza pustą listę).

To sprawia, że ​​kod jest nieco prostszy, ponieważ nie musisz sprawdzać, czy argument jest dostarczony (jak w wersji Daniela). Zrobiłem też kilka innych uproszczeń (tj. Wyliczyć width funkcjonalnie i użyj sekwencji zrozumiałych - jeśli Sheet nie modyfikuje danych, można również uniknąć używania ResizeArray):

type Sheet private (rows) =  
  // The main constructor is 'private' and so users do not see it,
  // it takes columns and calculates the maximal column length
  let width = rows |> Seq.map Seq.length |> Seq.fold max 0

  // The default constructor calls the main one with empty ResizeArray
  new() = Sheet(ResizeArray<_>())

  // An alternative constructor loads data from the file
  new(fileName:string) =
    let lines = System.IO.File.ReadLines fileName 
    Sheet(ResizeArray<_> [ for line in lines -> ResizeArray<_> (line.Split ',') ])

22
2017-08-21 15:41



to jest całkiem urocze - nicolas


rows i width nie są w zakresie. Możesz sprawić, że członkowie ustawiają / ustawiają je, lub (moje zalecenie) sprawiają, że konstruktor o większości argumentów jest najważniejszy:

type Sheet (fileName) =
  let rows = new ResizeArray<ResizeArray<string>>()
  let mutable width = 0

  do
    match fileName with 
    | null | "" -> ()
    | _ ->
      let lines = System.IO.File.ReadLines fileName
      for line in lines do
        let cells = line.Split ','
        rows.Add(new ResizeArray<string> (cells)) //line 16
        if cells.Length > width then width <- cells.Length

  new() = Sheet("")

Generalnie konstruktory pomocnicze mają być przeciążeniami podstawowego konstruktora, więc nie mogą wchodzić w interakcje z klasami wewnętrznymi (polami). To zachęca do jednej ścieżki inicjalizacji (i lepszego projektowania).


6
2017-08-21 14:09



Jak to możliwe, że nie są w zasięgu? Myślałem, że w tym przypadku powinny być polami klasy? - Grzenio
Są w zakresie 1) podstawowego konstruktora i 2) członków, a nie innych konstruktorów. - Daniel
Hmm, ciekawa decyzja projektowa ... W każdym razie, dzięki za wyjaśnienia. - Grzenio
Konstruktory pomocnicze są drugiej klasy w języku F #, co zachęca do pojedynczej ścieżki inicjalizacji. - Daniel
@ Daniel.Interesting uwagi. F # jest tutaj dość otwarty. - nicolas