locked
F# から、System.Collections.Generic.Listを利用する時 RRS feed

  • 質問

  • 例えば、F# から、System.Collections.Generic.List<string> を利用する時、「Max<>」「Min<>」等々、C#では使用できる候補が、F#では使用できません。

    この理由はなんなのか、教えていただけませんか。

    ちなみに、VSは2008、F#は2.0です。

    2010年7月11日 23:20

すべての返信

  • こんにちは。いげ太です。

    <del>Max<'T> などは Enumerable クラスに定義される拡張メソッドですが、C# で言うところの拡張メソッドを、拡張メソッドとして使用することは、F# ではできません。</del>
    => ごめんなさい、勘違いでした。

    # F# における拡張メソッドは、型拡張(Type Extensions)という別のしくみの上で実現されます。

    なので、拡張メソッドは単に静的メソッドとして使う必要があります。

    open System.Collections.Generic
    open System.Linq
    
    // 配列
    let arr = [| 4; 5; 2; 6; 1; 7; 3 |]
    
    // List<'T> のインスタンス化。
    // IDisposable でないクラスなら new を省略可能。
    // 'T に int と明示しなくても _ と置いて型推論に任せて OK。
    let lst = List<_>(arr)
    
    let x = Enumerable.Max(lst) // 7

    なお、F# では、このような場合 Seq モジュールを用いるのが一般的です。Seq モジュールは、関数型のコードによりよく馴染むように再定義された Enumerable クラスと考えるとよいでしょう。

    let x = Seq.max lst // 7

    拡張メソッドがないとメソッド チェーンできないと思われるかもですが、パイプライン演算子 '|>' を使用すれば流れるように書くことができます。ドットじゃなくてパイプで繋げば OK です。以下はサンプルです。

    lst                                // seq [ 4; 5; 2; 6; 1; 7; 3 ]
    |> Seq.filter (fun n -> n % 2 = 0) // seq [ 4; 2; 6 ]
    |> Seq.map (fun n -> n * 10)       // seq [ 40; 20; 60 ]
    |> Seq.sort                        // seq [ 20; 40; 60 ]
    

    > ちなみに、VSは2008、F#は2.0です。
    <del>VS 2008(.NET 2.0)向けの F# のバージョンは 1.9.9.9 です。VS 2010(.NET 4)向けの F# のバージョンが 2.0 になります。</del>
    => 失礼しました。おもいきり勘違いしてたようです。
    • 編集済み いげ太 2010年9月24日 1:13 間違いの訂正。C# の拡張メソッドは F# でも拡張メソッドとして使用できます。
    2010年7月12日 11:20
  • ありがとうございます。

    open System.Linq で、Enumerable の静的メソッド、がポイントですか。

    C#のページに、「拡張メソッドは特別な種類の静的メソッドですが、拡張された型のインスタンス メソッドのように呼び出します。」という記事がありました。これがらみと見てよいのでしょうか。

    今、C#のプログラムをF#に移行しているのですが、Dictionary、Hash、List等、を多用して、どんどん関数言語的なところから離れるような気がして、しかたがありません。

    超頻繁に、増減、変更が発生する配列要素は、そうするしかないように思うのですが。基本的に考え方が間違ってますでしょうか。 F#のList、Seqをうまく使う手があるのでしょうか。

    不要不急の趣味人です。お時間があったら、お教えください。

    ちなみに、MicrosoftResearchには

    Download F# 2.0 for Windows + Visual Studio 2008 (April 2010 release).

    がありますよね。(DotNet 3.5 です)

    Visual Studio 2010 Shell +F#2.0を待ち焦がれています。

    2010年7月12日 13:51
  • > F# 2.0 for Windows + Visual Studio 2008 (April 2010 release)

    失礼しました。なにか勘違いをしていたようです。

    本題ですが。

    ふつう、メソッド定義は、そのクラスの定義に含める必要があります。ところが、C# の拡張メソッドという機能を使用すると、別途、静的クラスの静的メソッドとして定義したものを、インスタンス メソッドであるかのように使用することができます。つまり今回の場合で言えば、Enumerable クラスの Max<'T> メソッドが、IEmumerable<'T> インターフェースに対する拡張メソッドとして定義されているので、C# では、IEnumerable<'T> インターフェースを実装した List<'T> クラスにおいて、そのインスタンス メソッドであるかのように Max<'T> メソッドなどを使うことができていた、ということです。<del>そして、この拡張メソッドが、F# では拡張メソッドとして使えません。</del>

    C# では、IEnumerable<'T> オブジェクトの操作には Enumerable クラスに定義された拡張メソッドを使いますが、F# では Seq モジュールの関数を使ってください。

    なお、F# では、System.Collections.Generic.List<'T> の別名として ResizeArray<'T> が定義されています。使っちゃダメなものをわざわざ別名定義しないでしょう。頻繁に追加や挿入およびサイズ変更が起きるようなコレクションに対しては、全然使っていって OK ではないかと思います。関数型っぽく書こうとして、でも関数型にうまく落としこめないような処理では気兼ねなくオブジェクト指向できるのは、F# の強みだと思いますし、またそれこそが F# 流なのかなーと。

    あと、F# には不変な辞書として Map<'Key,'Value> クラスがありますので、場合によっては Dictionary の代用としてこれを使うのも手かもしれません。

    Visual Studio 2010 Shell +F#2.0を待ち焦がれています。

    僕もです。ただ今のところ調査中とかで、続報も見かけないので、まだもうちょっと待たないといけないような雰囲気ですねぇ。

    • 編集済み いげ太 2010年9月24日 1:14 間違いの訂正
    2010年7月12日 15:50
  • 丁寧な回答、ありがとうございます。

    先達も仲間もなく、孤軍奮闘している老兵です。F#のお勉強を開始して、右も左もわからない時から、「FParsec」に偏向したコードばかり書いてまして、基本がわかっていません。今後もよろしくお願いします。

     

    2010年7月12日 22:33
  • > Max<'T> などは Enumerable クラスに定義される拡張メソッドですが、C# で言うところの拡張メソッドを、拡張メソッドとして使用することは、F# ではできません。

    すいません。思いっきり勘違いでした。C# の拡張メソッドを F# 側からも拡張メソッドとして使用できます。

    <del>IEnumerable に対する拡張メソッドが使用できないように見えていたのは、単にキャストしてないからでした(F# では、インターフェースへの暗黙のキャストは行われない)。つまり、</del>以下のようにすれば OK でした。
    =>ごめんなさい。暗黙のキャストうんぬんは本件においては関係ないです。

    open System.Collections.Generic
    open System.Linq
    
    let lst = List<_>( [| 4; 5; 2; 6; 1; 7; 3 |] )
    let x = (lst :> seq<_>).Max() // [訂正] lst.Max() で OK です

    大変失礼しました。

    # 以前のバージョンではサポートされてなかったのですが、
    # VS 2010 Beta2(?)あたりからサポートされたようです。
    • 編集済み いげ太 2010年11月9日 2:24
    2010年9月24日 1:22
  • 思いっきり、まぬけな反応で申し訳ありません。
    色々回答くださっているのに、1ヶ月以上見てませんでした。
    基本的には、「Linq」がらみということで、「DotNet3.0」以降に対応した「F#」で対応可能、ということでしょうか。

    > 暗黙のキャストは行われない

    最近、これでうんざりしてます。

    2010年11月5日 9:26
  • 昨日来、2年も前の、拡張メソッドやLINQの(C#の)お勉強のリストを見直してたんです。
    MSDNの「Enumerable メンバー」の解説には、「IEnumerable(T) を実装するオブジェクトをクエリするための一連の static メソッドを供給する」とありますが。
    要は「System.Linq」をインポートしたから、これらが使えたんじゃないんかな、というわけで、アップキャストなしでやってみました。

    > open System.Collections.Generic
    - open System.Linq;;
    > let lst = List( [| 4; 5; 2; 6; 1; 7; 3 |] );;

    val lst : List<int>

    > lst.Max();;
    val it : int = 7

    別に「seq」にアップキャストしなくても、とおってしまいますよね。
    拡張メソッドだから、インスタンスで呼び出せるし。
    それなら、「Enumerable メンバー」メソッドはなんでも使えちゃうのかな、とおもって

    > lst.Where(fun v -> v < 6);;
    val it : seq<int> = seq [4; 5; 2; 1; ...]
    >

    うーーん、戻り値はseq<int>だけど、とおってしまう。

    なんか「仁義なきLINQ」という感じなんですが。

     

    2010年11月6日 1:32
  • 再三にわたり間違い情報ばっかり書いてしまって本当に申し訳ないです。反省しきりです。

    > 要は「System.Linq」をインポートしたから

    はい。その通りです。いま現在の F# においては、open System.Linq とするだけで Max や Min や Where など、IEnumerable<'T> 向けの拡張メソッド群を使えるようになっています。

    2010年11月9日 2:42