質問者
F# から、System.Collections.Generic.Listを利用する時

質問
すべての返信
-
こんにちは。いげ太です。
<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 -
> 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 -
昨日来、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