【问题标题】:Rewriting an odd function重写奇函数
【发布时间】:2011-07-06 22:05:47
【问题描述】:

我有如下函数,感觉有点奇怪。可能有一个我正在复制的习语或模式已经内置。该函数应该延迟返回加载的程序集及其引用。在每次调用时,它应该返回迄今为止发现的程序集的完整列表,以及新“发现”的程序集,直到迭代停止。有没有更好的写法?

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() 的递归遍历相同的内容。谁能证实这一点?如果是这样,更直接的解决方案是可能的。

【问题讨论】:

  • LazyListSeq.cache 将帮助您编写此代码,而无需将生成的程序集存储在 HashSet 中(您可以递归访问到目前为止生成的程序集以测试您是否已经生成了该程序集) .但是,使用HashSet 效率更高,因此它似乎是一种更好的方法。

标签: reflection f#


【解决方案1】:

我认为在 F# 库中的任何地方都没有可以捕获这种模式的函数。但是,它绝对感觉像是可能有用的东西。可重用模式是使用指定的“生成函数”从几个初始元素的集合中生成某个集合的所有元素(数学家可能会称其为生成函数的不动点)。

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

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)

【讨论】:

  • 感谢您如此清晰地表达模式......以及实施。
  • @Daniel - 谢谢,虽然我没有真正回答你的问题。感觉在 F# 库中的某个地方可能会有一些有用的东西来实现这个......
  • 如果您遇到问题,请更新您的答案。目前,这将使我能够充分简化代码。
  • floodfill 将是该算法的适当描述。
  • @Tomas:在这里使用HashIdentity.Structural重要吗?
【解决方案2】:

我的函数(以及相应的 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-10-18
    • 1970-01-01
    • 1970-01-01
    • 2012-03-06
    • 2016-11-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多