如何在F#中检索元组的头部和尾部

时间:2011-11-07 09:21:08

标签: f# tuples

  1. 如何在F#中检索元组的头部和尾部?

    例如Conj (a, b),头部为Conj,尾部为(a, b)

  2. 我想在每个参数上递归运行buildtree函数,将头部作为Node的元素,F#中的地图在哪里?

    let rec getparams = map List.head (List.tail getparams);
    
    type Elem = Prop
    type Tree = E | T of Elem * Tree * Tree
    
    let rec buildtree vars = function
        | E = head vars
        | T = buildtree (getparams vars)
    
  3. 更新后:

    open System
    open Microsoft.FSharp.Reflection
    // Learn more about F# at http://fsharp.net
    //type Prop = {a: string; b: string}
    //let Prop a b = (a, b) 
    type Op = Prop
    type tree = E | T of Op * tree * tree
    let tree x y z = (x, y, z)
    
    type binOp = Conj | Disj | Impl 
    type expr =   
    | Prop of string   
    | BinOp of binOp * expr * expr 
    | Conj of expr * expr
    | Disj of expr * expr
    | Impl of expr * expr
    
    type Prop = {a: string}
    let Prop a = (a)
    
    //type Conj = {a : Prop; b : Prop} 
    let Conj a b = (a, b)
    
    //type Conj_Int = {a : Prop; b : Prop} 
    let Conj_Int a b = Conj a b
    //type Conj_Elmin1 = {a : Conj}
    let Conj_Elmin1 a = fst a
    //type Conj_Elmin2 = {a : Conj}
    let Conj_Elmin2 a = snd a
    
    //type Impl = {a : Prop; b : Prop} 
    let Impl a b = (a b)
    //type Impl_Int = {assume : Prop; b : Prop} 
    let Impl_Int assume b = Impl assume b
    //type Impl_Elmin = {a :string; b : Impl}
    let Impl_Elmin a b = if a = fst b then snd b
    
    type Neg = {a : Prop;}
    let Neg a = (a)
    
    //type Double_Neg_Int = {a : Prop;}
    let Double_Neg_Int a = Neg(Neg(a))
    
    //type Double_Neg_Elmin = {a : Prop}
    let Double_Neg_Elmin a = fst(fst(a))
    
    //type Disj = {a : Prop; b : Prop} 
    let Disj a b = (a,b)
    
    //type Disj_Int1 = {a : Prop; b : Prop} 
    let Disj_Int1 a b = (a b)
    //type Disj_Int2 = {a : Prop; b : Prop} 
    let Disj_Int2 a b = (a b)
    
    //type Disj_Elmin1 = {a : Disj}
    let Disj_Elmin1 a = fst(a)
    //type Disj_Elmin2 = {a : Disj}
    let Disj_Elmin2 a = snd(a)
    
    type TupleSplitter = static member splitTuple (a,b,c) = (a,(b,c)) 
    
    let tupleToList t = if Microsoft.FSharp.Reflection.FSharpType.IsTuple(t.GetType()) then Some (Microsoft.FSharp.Reflection.FSharpValue.GetTupleFields t |> Array.toList) else None
        let operation x = List.head(List.ofSeq(FSharpValue.GetTupleFields(x)))
    let parameters x = List.tail(List.ofSeq(FSharpValue.GetTupleFields(x)))
    
    
    let rec map f = function | Prop _ as t -> f t | BinOp(op, a, b) -> f(BinOp(op, map f a, map f b))
    (*
    let rec map f = function   
    | Prop _ as t -> f t   | Conj(a, b) -> f(Conj(map f a, map f b))   
    | Disj(a, b) -> f(Disj(map f a, map f b))   
    | Impl(a, b) -> f(Impl(map f a, map f b)) 
    *)
    let buildtree vars expr = map (function Prop v -> Map.find v vars | expr -> expr) expr 
    
    let t = buildtree(Conj("a","b"))
    
    1. 如何有两种类型的表达式Op * Tree * Tree和Op * Tree?

3 个答案:

答案 0 :(得分:5)

正如Ankur所说,你不能得到一个元组的头和尾 - 这些操作是为处理具有任意长度的功能列表而设计的,并且不能为编译时已知长度的元组定义。如果你想要任意长度的数据,你应该使用元组和模式匹配(或List.headList.tail)。

如果您确实需要动态处理元组,可以使用F#reflection:

open Microsoft.FSharp.Reflection

(1,2,3)
|> FSharpValue.GetTupleFields // Get fields of tuple as an array
|> List.ofSeq                 // Convert array to a list
|> List.tail                  // Now you can process list using head/tail

但请注意,反射通常有点慢,只应在需要时使用(即编写一些动态且无法以其他方式编写的代码时)。

答案 1 :(得分:3)

元组定义为(exp1,exp2,...,expn),例如(1,“2”,“3”)。 我在你的代码中看不到这种模式。

如果使用(exp1 exp2),则表示函数应用程序(将exp2应用为函数exp1的第一个参数)。 您在代码中看到的错误是因为您将Conj定义为接受函数作为第一个参数的函数,并且您传递了一个字符串(“a”)而不是函数。

如果您的问题是如何分割头部和尾部的元组,您可以选择Tomas刚才解释的动态方法,它适用于任何n元组,但您将丢失类型信息。

否则强类型解决方案仅基于模式匹配:

let splitTuple (a,b,c) = (a,(b,c))
// Usage
let (head,tail) = splitTuple (1,"2",'3')

如果你想让它适用于n元组,你必须为每个n定义一个重载:

type TupleSplitter =
  static member splitTuple (a,b,c) = (a,(b,c))
  static member splitTuple (a,b,c,d) = (a,(b,c,d))
  static member splitTuple (a,b,c,d,e) = (a,(b,c,d,e))
// ... more overloads, as much as you need

// Usage
let (head,tail) = TupleSplitter.splitTuple (1,"2",'3',4.0)
// val tail : string * char * float = ("2", '3', 4.0)
// val head : int = 1

答案 2 :(得分:3)

您似乎试图在F#中复制Haskell语法和语义。不要那样做。查看现有的ML代码并学习如何解决您的问题。换句话说,你的问题是一个XY问题:你问的是错误的问题。

在不知道您要解决的问题的情况下,很难回答您的问题,但最好的猜测是:

type Expr = 
  | Prop of string
  | Conj of Expr * Expr
  | Disj of Expr * Expr
  | Impl of Expr * Expr

let deConj = function
  | Conj(a, b) -> a, b
  | _ -> invalidArg "expr" "deConj"

也许你想在map类型上写expr

let rec map f = function
  | Prop _ as t -> f t
  | Conj(a, b) -> f(Conj(map f a, map f b))
  | Disj(a, b) -> f(Disj(map f a, map f b))
  | Impl(a, b) -> f(Impl(map f a, map f b))

另一种解决方案是重写您的类型以分解运算符:

type binOp = Conj | Disj | Impl
type expr =
  | Prop of string
  | BinOp of binOp * expr * expr

let rec map f = function
  | Prop _ as t -> f t
  | BinOp(op, a, b) -> f(BinOp(op, map f a, map f b))

修改

我不确定你的buildtree函数应该做什么但是如果它正在评估表达式那么你可能想要这样的东西:

let buildtree vars expr =
  map (function Proj v -> Map.find v vars | expr -> expr) expr

这会将一个表达式映射到另一个表达式,将Proj v替换为v给出的相应表达式(即变量vars的值)。