2

在下面的代码Seq.generateUnique中被限制为 type ((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

我希望它是通用的,但是将其放入文件中而不是使用它会产生值限制错误:

价值限制。值 'generateUnique' 已被推断为具有通用类型 val generateUnique : (('_a -> '_b) -> '_c -> seq<'_a>) 当 '_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需要成为一个句法参数(也就是说,我不认为对不同的 sgenerateUnique使用相同的参数是有意义的)。因此,一个简单的解决方法是:HashSetf

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 回答