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

ジェネリックにしたいのですが、使用法とは別にファイルに入れると、値制限エラーが発生します。

値制限。'_b :> seq<'_a> および '_c : > seq<'_a> 'generateUnique' への引数を明示的にするか、ジェネリックにするつもりがない場合は、型注釈を追加します。

明示的な型パラメーター ( let generateUnique<'T> = ...) を追加するとエラーは解消されますが、異なる結果が返されるようになりました。

型パラメーターなしの出力 (望ましい/正しい動作):

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

そして:

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

行動が変わるのはなぜ?関数をジェネリックにして、目的の動作実現するにはどうすればよいですか?

4

2 に答える 2

3

私はあなたの定義が完全に正しいとは思いません.それはf構文上の議論である必要があるように私generateUniqueには思えHashSetます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
于 2011-07-07T19:56:23.170 に答える
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
于 2011-07-07T20:07:50.993 に答える