我有以下功能,感觉有点奇怪。可能有一个成语或模式,我正在复制已经内置的。该函数应该懒惰地返回加载的程序集及其引用。每次调用时,它应返回到目前为止发现的程序集的完整列表,以及新发现的程序集,直到迭代停止。有没有更好的方法来写这个?
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
}
顺便说一句,我还没有对它进行过测试,而且它很有可能是它的错误。因此,依赖于描述而不是代码。
似乎LazyList
或Seq.cache
是合适的,但GetAssemblies()
是不确定的。但是,似乎一旦加载了所有引用的程序集,GetAssemblies()
将返回与GetReferencedAssemblies()
的重复遍历相同的内容。谁能证实这一点?如果是这种情况,可以采用更直接的解决方案。
答案 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