重写一个奇怪的函数

时间:2011-07-06 22:05:47

标签: reflection f#

我有以下功能,感觉有点奇怪。可能有一个成语或模式,我正在复制已经内置的。该函数应该懒惰地返回加载的程序集及其引用。每次调用时,它应返回到目前为止发现的程序集的完整列表,以及新发现的程序集,直到迭代停止。有没有更好的方法来写这个?

let discoverAssemblies =
  let known = HashSet()
  let rec discover (assemblies:array<Assembly>) =
    seq {
      for asm in assemblies do
        if known.Add(asm) then
          yield asm
          let refs = 
            asm.GetReferencedAssemblies() 
            |> Array.map (fun asmName -> Assembly.Load(asmName))
          yield! discover refs
    }
  fun () ->
    seq {
      for asm in known do yield asm
      yield! AppDomain.CurrentDomain.GetAssemblies() |> discover
    }

顺便说一句,我还没有对它进行过测试,而且它很有可能是它的错误。因此,依赖于描述而不是代码。

修改

似乎LazyListSeq.cache是合适的,但GetAssemblies()是不确定的。但是,似乎一旦加载了所有引用的程序集,GetAssemblies()将返回与GetReferencedAssemblies()的重复遍历相同的内容。谁能证实这一点?如果是这种情况,可以采用更直接的解决方案。

2 个答案:

答案 0 :(得分:2)

我不认为有一个函数会在F#库中的任何地方捕获这个模式。但是,它确实感觉像是有用的东西。可重用模式是使用指定的“生成函数”从一些初始元素的集合中生成某些集合的所有元素(数学家可能将其称为生成函数的固定点)。 / p>

在这样的可重用函数中捕获模式可能是值得的:

module Seq =
  /// Uses the specified generating function 'f' to generate items
  /// from each of the specified initial values. Then continues
  /// generating values repeatedly until all values are produced.
  let generateUnique f (initial:seq<'T>) =
    let known = HashSet()
    let rec loop initial = seq {
      for itm in initial do
        if known.Add(itm) then
          yield itm
          yield! loop (f itm) }
    loop initial

然后你可以编写你的函数来使用Seq.generateUniqe发现程序集,如下所示:

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

答案 1 :(得分:0)

我的功能(反过来,Tomas的优秀实现)没有所需的行为。如果迭代在所有引用的程序集产生之前停止,则可能永远不会访问其余的引用(因为引用程序集已经“已知”)。

我最终得到了:

module Seq =
  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

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