Переписать нечетную функцию - PullRequest
1 голос
/ 07 июля 2011

У меня есть следующая функция, которая кажется немного странной.Может быть, идиома или шаблон, который я копирую, уже встроен.Функция должна лениво возвращать загруженные сборки и их ссылки.При каждом вызове он должен возвращать полный список обнаруженных сборок, а также вновь «обнаруженные» сборки до тех пор, пока итерация не остановится.Есть ли лучший способ написать это?

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().Кто-нибудь может это подтвердить?В этом случае возможны более простые решения.

Ответы [ 2 ]

2 голосов
/ 07 июля 2011

Я не думаю, что есть функция, которая захватила бы этот шаблон где-нибудь в библиотеке 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)
0 голосов
/ 07 июля 2011

Моя функция (и, в свою очередь, отличная реализация Томаса) не имеет желаемого поведения. Если итерация останавливается до получения всех ссылочных сборок, возможно, что оставшиеся ссылки никогда не будут посещены (поскольку ссылочная сборка уже «известна»).

Я закончил с:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...