2011-07-07 6 views
2

Seq.generateUniqueのコードは((Assembly -> seq<Assembly>) -> seq<Assembly> -> seq<Assembly>)であることが制限されています。さらに別の値の制限質問

open System 
open System.Collections.Generic 
open System.Reflection 

module Seq = 
    let generateUnique = 
    let known = HashSet() 
    fun f initial -> 
     let rec loop items = 
     seq { 
      let cachedSeq = items |> Seq.filter known.Add |> Seq.cache 
      if not (cachedSeq |> Seq.isEmpty) then 
      yield! cachedSeq 
      yield! loop (cachedSeq |> Seq.collect f) 
     } 
     loop initial 

let discoverAssemblies() = 
    AppDomain.CurrentDomain.GetAssemblies() :> seq<_> 
    |> Seq.generateUnique (fun asm -> asm.GetReferencedAssemblies() |> Seq.map Assembly.Load) 

let test() = printfn "%A" (discoverAssemblies() |> Seq.truncate 2 |> Seq.map (fun asm -> asm.GetName().Name) |> Seq.toList) 
for _ in 1 .. 5 do test() 
System.Console.Read() |> ignore 

私はそれが一般的なように好きですが、その使い方とは別にファイルにそれを置くことは値制限エラーを生成したい:明示的な型パラメータ(let generateUnique<'T> = ...)を追加

Value restriction. The value 'generateUnique' has been inferred to have generic type val generateUnique : (('_a -> '_b) -> '_c -> seq<'_a>) when '_b :> seq<'_a> and '_c :> seq<'_a> Either make the arguments to 'generateUnique' explicit or, if you do not intend for it to be generic, add a type annotation.

はエラーを排除しかし、今は別の結果を返します。 typeパラメータ(正しい/希望の動作)なし

出力:

["mscorlib"; "TEST"] 
["FSharp.Core"; "System"] 
["System.Core"; "System.Security"] 
[] 
[] 

とと:

["mscorlib"; "TEST"] 
["mscorlib"; "TEST"] 
["mscorlib"; "TEST"] 
["mscorlib"; "TEST"] 
["mscorlib"; "TEST"] 

なぜ行動変容していますか?どのようにして汎用機能を目的の動作にすることができますか?

+0

Seq.distinctByを使用しないのはなぜ? – Huusom

+0

@Huusom:もう少し詳しいことがあります。それは、 'distinct' + recursive' collect' + memoizationのようなもので、それらの間に微妙な相互依存性があります。 – Daniel

答えて

3

generateUniqueたくさん標準memoizeパターンのようなものです:実際のキャッシュ自体をしないで、通常の機能からメモ化関数を計算するのに使用されなければなりません。

@kvbは、このシフトのために必要な定義の変更について正しかったが、その後、あなたは次のようにdiscoverAssembliesの定義を変更する必要があります。

let discoverAssemblies = 
    //"memoize" 
    let generator = Seq.generateUnique (fun (asm:Assembly) -> asm.GetReferencedAssemblies() |> Seq.map Assembly.Load) 

    fun() -> 
     AppDomain.CurrentDomain.GetAssemblies() :> seq<_> 
     |> generator 
+0

これは動作し、実際にはバージョンを明示的な型パラメータで修正し、kvbの変更の必要性を否定します。 – Daniel

+0

クールだけど、関数の@kvbs版を使うべきだと思います。なぜなら、 'f'(各' f'の新鮮な 'HashSet')を"メモする "からです。明示的な型パラメータを持つバージョンは、型ごとに1つの 'HashSet'! –

+0

ああ、そうです。ありがとう! – Daniel

3

私はあなたの定義がかなり正しいとは思わない:fgenerateUniqueの構文上の引数である必要があると私には思われます(つまり、同じものを使用することは意味がないと思います。 f)。したがって、簡単な修正は次のとおりです。

let generateUnique f =  
    let known = HashSet()  
    fun initial ->  
     let rec loop items =   
      seq {   
       let cachedSeq = items |> Seq.filter known.Add |> Seq.cache   
       if not (cachedSeq |> Seq.isEmpty) then    
        yield! cachedSeq    
        yield! loop (cachedSeq |> Seq.collect f)   
      }  
     loop initial 
+0

これは、typeパラメータの有無にかかわらず、後者の不正な出力を生成します。私は 'f'が非決定論的であることを期待しているので、それを内部関数に渡しています(それが正当な理由であるかどうかはわかりません)。 – Daniel