16

次のように、追加の作業を行う (つまり、基本的な csv ファイルを読み取る) 追加のコンストラクターを F# で作成しようとしています。

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

しかし、次のエラーが発生します。

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

私は何を間違っていますか?

4

2 に答える 2

32

Daniel が指摘したように、F# の設計では、通常、クラスが必要とするすべての引数を受け取り、初期化を実行する 1 つのメイン コンストラクターがあります。他のコンストラクターは、引数のデフォルト値を提供するか、他の情報からそれらを計算できます。

rowsあなたの場合、最良の設計はコンストラクター引数として渡すことだと思います。次に、2 つのコンストラクターを追加できます (1 つはファイルをロードし、もう 1 つは空のリストを提供します)。

これにより、引数が指定されているかどうかを確認する必要がないため (ダニエルのバージョンのように)、コードが少し単純になります。他にもいくつかの単純化を行いました (つまりwidth、関数を計算し、シーケンス内包表記を使用Sheetします。データを変更しない場合は、使用を避けることもできます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 ',') ])
于 2012-08-21T15:41:57.987 に答える
6

rowswidth対象外です。メンバーを作成してそれらを取得/設定するか、(私の推奨) 引数が最も多いコンストラクターをプライマリにすることができます。

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("")

一般に、二次コンストラクターは一次コンストラクターのオーバーロードであることを意図しているため、クラスの内部 (フィールド) と相互作用することはできません。これにより、初期化のパスが 1 つ (およびより良い設計) になります。

于 2012-08-21T14:09:18.797 に答える