代码之家  ›  专栏  ›  技术社区  ›  Nathan Shively-Sanders

f文档是否有按类型搜索函数的方法?

  •  19
  • Nathan Shively-Sanders  · 技术社区  · 14 年前

    假设我想知道f是否具有类型的库函数

    ('T -> bool) -> 'T list -> int
    

    也就是说,计算一个函数返回真值的列表中有多少项。(或返回第一个返回“真”的项的索引)

    我以前在MSR网站上使用的大列表用于f,在有关msdn的文档准备就绪之前。我可以在页面上搜索上面的文本,因为上面列出了类型。但是现在,msdn文档只在单独的页面上列出类型——模块页面是一堆描述性的文本。谷歌有点管用,但它帮不了你

    // compatible interfaces
    ('T -> bool) -> Seq<'T> -> int
    // argument-swaps
    Seq<'T> -> ('T -> bool) -> int
    // type-variable names
    ('a -> bool) -> Seq<'a> -> int
    // wrappers
    ('a -> bool) -> 'a list -> option<int>
    // uncurried versions
    ('T -> bool) * 'T list -> int
    // .NET generic syntax
    ('T -> bool) -> List<'T> -> int
    // methods
    List<'T> member : ('T -> bool) -> int
    

    haskell有一个独立的程序 Hoogle . F是否有一个等价物,比如说F或其他类似物?

    3 回复  |  直到 8 年前
        1
  •  17
  •   kvb    14 年前

    我不知道有这样的工具。不过,使用 System.Reflection (或者更好的是,Powerpack中的元数据库),这样您就可以考虑等价模块类型变量名等。

    编辑 -我是对的-它 一个有趣的练习。接下来的内容有很多缺点,但对于大约150行代码来说也不算太糟糕。希望这足以让那些想使用真正工具的人开始工作。它不做任何高级工作,比如检查带有重新排序参数的函数,并且元数据库对于使用完全限定的名称有点挑剔,所以您需要小心一点。为了回答你原来的问题,我执行了

    find "('a -> Microsoft.FSharp.Core.bool) -> Microsoft.FSharp.Collections.list`1<'a> -> Microsoft.FSharp.Core.int" 
    

    并得到以下候选人名单:

    Microsoft.FSharp.Core.Operators.( + )
    Microsoft.FSharp.Core.Operators.( - )
    Microsoft.FSharp.Core.Operators.( * )
    Microsoft.FSharp.Core.Operators.( / )
    Microsoft.FSharp.Core.Operators.( % )
    Microsoft.FSharp.Core.Operators.sqrt
    Microsoft.FSharp.Core.LanguagePrimitives.EnumOfValue
    Microsoft.FSharp.Core.LanguagePrimitives.EnumToValue
    Microsoft.FSharp.Core.LanguagePrimitives.AdditionDynamic
    Microsoft.FSharp.Core.LanguagePrimitives.CheckedAdditionDynamic
    Microsoft.FSharp.Core.LanguagePrimitives.MultiplyDynamic
    Microsoft.FSharp.Core.LanguagePrimitives.CheckedMultiplyDynamic
    Microsoft.FSharp.Core.LanguagePrimitives.GenericZero
    Microsoft.FSharp.Core.LanguagePrimitives.GenericOne
    Microsoft.FSharp.Collections.List.find
    Microsoft.FSharp.Collections.List.findIndex
    Microsoft.FSharp.Collections.List.maxBy
    Microsoft.FSharp.Collections.List.minBy
    

    其中,只有 List.findIndex 完全具有您要查找的泛型类型,但具有正确的类型参数组合,其他类型参数也是如此(例如,如果 'a = int 然后 List.find 具有所需类型)。不幸的是,在搜索中没有考虑约束,因此- List 函数不能实际匹配。

    不需要进一步的ADO,这里是我使用的代码——您需要添加对fsharp.powerpack.metadata程序集的引用,以使其正常工作。

    open Microsoft.FSharp.Metadata
    open System.Text.RegularExpressions
    
    (* type parameters let us switch out representation if need be *)
    type Tag<'ty> = | Tuple | Arr | Ground of 'ty
    type Ty<'ty,'a> = Param of 'a | Complex of Tag<'ty> * Ty<'ty,'a> list
    
    (* Gets something stable from an FSharpEntity so that we can see if two are identical *)
    let rec getType (e:FSharpEntity) =
      if (e.IsAbbreviation) then
        getType e.AbbreviatedType.NamedEntity
      else
        e.ReflectionType
    
    (* FSharpType -> Ty<System.Type,string> *)
    let rec cvt (e:FSharpType) =
      if e.IsTuple then
        Complex(Tuple, e.GenericArguments |> Seq.map cvt |> List.ofSeq)
      elif e.IsFunction then
        Complex(Arr, e.GenericArguments |> Seq.map cvt |> List.ofSeq)
      elif e.IsGenericParameter then
        Param e.GenericParameter.Name
      else
        Complex(Ground(e.NamedEntity |> getType), e.GenericArguments |> Seq.map cvt |> List.ofSeq)
    
    (* substitute type for variable within another type *)
    let rec subst v t = function
    | Complex(tag,l) -> Complex(tag, l |> List.map (subst v t))
    | Param i when i = v -> t
    | Param j -> Param j
    
    (* get type variables used in a type *)
    let rec usedVars = function
    | Param i -> Set.singleton i
    | Complex(tag, l) -> Set.unionMany (List.map usedVars l)
    
    (* Find most general unifier (if any) for two types *)
    let mgu t1 t2 =
      let rec mgu subs = function
      | [] -> Some subs
      | (Complex(tag1,l1),Complex(tag2,l2))::rest ->
           if tag1 <> tag2 then
             None
           else
             let rec loop r = function
             | [],[] -> mgu subs r
             | [],_ | _,[] -> None
             | x::xs, y::ys -> loop ((x,y)::r) (xs,ys)
             loop rest (l1,l2)
      | (Param i, Param j)::rest when i = j -> mgu subs rest
      | ((Param i, x) | (x, Param i))::rest ->
           if (Set.contains i (usedVars x)) then
             None (* type would be infinite when unifying *)
           else
             mgu ((i,x)::subs) (rest |> List.map (fun (t1,t2) -> (subst i x t1, subst i x t2)))
      mgu [] [t1,t2]
    
    (* Active patterns for parsing - this is ugly... *)
    let (|StartsWith|_|) r s =
      let m = Regex.Match(s, r)
      if m.Success && m.Index = 0 then
        Some(m.Value, s.Substring(m.Length))
      else None
    
    let rec (|Any|) (|P|_|) = function
    | P(x,Any (|P|_|) (l,r)) -> x::l, r
    | s -> [],s
    
    let rec (|Any1|_|) (|P|_|) = function
    | P(x,Any (|P|_|) (l,r)) -> Some(x::l, r)
    | _ -> None
    
    let (|Seq|_|) (|P|_|) (|Q|_|) = function
    | P(x,Q(y,r)) -> Some((x,y),r)
    | _ -> None
    
    let (|Choice|_|) (|P|_|) (|Q|_|) = function
    | P(p) -> Some p
    | Q(p) -> Some p
    | _ -> None
    
    let (|Delimit|_|) s (|P|_|) = function
    | P(x,Any ((|Seq|_|) ((|StartsWith|_|) s) (|P|_|)) (l,r)) -> Some(x::(List.map snd l), r)
    | _ -> None
    
    let (|Delimit1|_|) s (|P|_|) = function
    | P(x,StartsWith s (_,Delimit s (|P|_|) (l,r))) -> Some(x::l, r)
    | _ -> None
    
    (* Basically a BNF grammar for types *)
    let rec (|TyE|_|) = function
    | ArrE(p) | TupleE(p) | AtomE(p) -> Some(p)
    | _ -> None
    and (|ArrE|_|) = function
    | Choice (|TupleE|_|) (|AtomE|_|) (dom,StartsWith "->" (_,TyE(rng,r))) -> Some(Complex(Arr,[dom;rng]), r)
    | _ -> None
    and (|TupleE|_|) = function
    | Delimit1 @"\*" (|AtomE|_|) (l,r) -> Some(Complex(Tuple,l), r)
    | _ -> None
    and (|AtomE|_|) = function
    | ParamE(x,r) | GroundE(x,r) | StartsWith @"\(" (_,TyE(x,StartsWith @"\)" (_,r))) -> Some(x,r)
    | _ -> None
    and (|ParamE|_|) = function
    | StartsWith "'[a-zA-Z0-9]+" (s,r) -> Some(Param s, r)
    | _ -> None
    and (|GroundE|_|) = function
    | StartsWith "[`.a-zA-Z0-9]+" (gnd, StartsWith "<" (_, Delimit "," (|TyE|_|) (l, StartsWith ">" (_,r)))) -> 
          let ty = FSharpAssembly.FSharpLibrary.GetEntity gnd |> getType
          Some(Complex(Ground(ty), l), r)
    | StartsWith "[`.a-zA-Z0-9]+" (gnd, r) ->
          let ty = FSharpAssembly.FSharpLibrary.GetEntity gnd |> getType
          Some(Complex(Ground(ty), []), r)
    | _ -> None
    
    (* parse a string into a type *)
    let parse (s:string) =
      (* remove whitespace before matching *)
      match s.Replace(" ","") with
      | TyE(ty,"") -> ty
      | _ -> failwith "Not a well-formed type"
    
    (* an infinite stream of possible variable names - for performing renaming *)
    let rec names = 
      let letters = ['a' .. 'z'] |> List.map string
      seq {
        yield! letters
        for n in names do
          for l in letters do
            yield n + l
      }
    
    (* finds entities in the F# library with the requested signature, modulo type parameter unification *)
    let find s =
      let ty = parse s
      let vars = usedVars ty
      seq {
        for e in FSharpAssembly.FSharpLibrary.Entities do
        for m in e.MembersOrValues do
          (* need try/catch to avoid error on weird types like "[]`1" *)
          match (try Some(cvt m.Type) with _ -> None) with
          | Some ty2 ->
            (* rename all type variables from the query to avoid incorrectly unifying with type variables in signatures *)
            let used = usedVars ty2
            let newVars = Seq.choose (fun v -> if Set.contains v used then None else Some(Param v)) names
            let varMap = Map.ofSeq (Seq.zip vars newVars)
            let ty = Map.fold (fun t v p -> subst v p t) ty varMap
            match mgu ty ty2 with
            | None -> ()
            | Some _ -> yield sprintf "%s.%s.%s" e.Namespace e.DisplayName m.DisplayName 
          | _ -> () }
    
        2
  •  10
  •   Nathan Shively-Sanders    14 年前

    基于kvb的答案,我创建了一个完整的应用程序。它在Github上的 http://github.com/sandersn/fing .

    代码仍然很难看,但它适用于简单的情况。我拿出了KVB最通用的统一器( mgu )因为它增加了很多不明显的结果。诸如结构约束和大多数通用超类型之类的花哨的东西也不起作用。

    如果不想从源代码构建,那么对于命令行版本也有二进制代码。(尽管如此,它仍然需要安装.NET运行时的现代版本。)最终,我会找到一些ASP.NET宿主,学习ASP,并将整个过程打包到Web应用程序中,这样就根本不需要安装了。(我想如果有需求的话,我可以创建一个客户端的GUI,但是我在这方面的经验甚至更少。)

        3
  •  5
  •   Ruben Bartelink    8 年前

    这是最新最伟大的: http://fsdn.azurewebsites.net/

    从文档中: https://github.com/fsdn-projects/FSDN

    支持的API签名

    API signature                     Query example
    Functions and values in modules   int -> string
    Fields of records and structs     Ref<'a> => 'a
    Methods and properties            'a list -> int or 'a list => int
    Constructors                      string -> Uri
    Names (function and method names) head : 'a list -> 'a
    Active patterns                   (||) : ... -> Expr -> ?