Еще один вопрос ограничения ценностей - PullRequest
2 голосов
/ 07 июля 2011

В следующем коде Seq.generateUnique ограничено типом ((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.> 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"]

Почему меняется поведение?Как сделать так, чтобы общие функции и достигли желаемого поведения?

Ответы [ 2 ]

3 голосов
/ 08 июля 2011

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
3 голосов
/ 07 июля 2011

Я не думаю, что ваше определение является совершенно правильным: мне кажется, что f должен быть синтаксическим аргументом для generateUnique (то есть я не верю, что имеет смысл использовать то же самое HashSet для разных f с). Поэтому простое исправление:

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
...