F#:递归函数:连接2个具有共同元素的列表

时间:2016-01-30 06:51:40

标签: list recursion f#

所以这就是我到目前为止所拥有的。感觉很接近,但我不确定如何解决第84行中的问题(第2行到最后一行:elif List.append(isolate(a),isolate(b))!= [] then List.append(isolate(a),隔离(b)中))。

    (* val isolate : l:'a list -> 'a list when 'a : equality *)
    let rec isolate (l:'a list) = 
        match l with
        | [] -> []
        | x::xs ->
            if memberof(x,xs)
            then
                let xs = remove (x,l)
                isolate xs
            else isolate xs
    ( * val common : 'a list * 'a list -> 'a list when 'a : equality *)
    let rec common (k: 'a list, l:'a list) = 
        match ((k:'a list),(l:'a list)) with
        | (a, b) -> 
            if a=[] then []
            elif b=[] then []
            elif List.append(isolate(a),isolate(b)) != [] then List.append(isolate(a),isolate(b))
            else []

编辑

要求发布完整的代码:

(* val sumlist : l:float list -> float *)
let rec sumlist l =  
    match (l:float list) with
    | [] -> 0.0
    | a::x -> (sumlist x) + a
    (* :: creates a list. *)
sumlist([1.0;2.0;3.0])
(* val squarelist : l:float list -> float list *)
let rec squarelist l = 
    match (l:float list) with
    | [] -> []
    | a::x -> (a*a)::(squarelist x)

(* val mean : l:float list -> float *)
let mean l = 
    match (l:float list) with
    | [] -> 0.0
    | l -> (sumlist l)/(float)l.Length
mean([1.0;2.0;3.0])
(* val mean_diffs : l:float list -> float list *)
let mean_diffs l = 
    match l with
    set a = mean(l)

    | [] -> []
    let rec diffs (a,l)=
        match l with
        | x::xs -> (x-(mean(l))::diffs(xs)
        | [] -> l
mean_diffs([1.0;2.0;3.0])

(* val variance : l:float list -> float *)
let variance l = 
    match (l:float list) with
    | [] -> 0.0
    | l -> (sumlist (squarelist (mean_diffs l)))/(float)l.Length


(* End of question 1 *) (* Do not edit this line. *)

(* Question 2 *) (* Do not edit this line. *)

(* val memberof : 'a * 'a list -> bool when 'a : equality *)
let rec memberof l=
    match (l: 'a * 'a list) with
    | (t,[]) -> false
    | (t, x::xs) when t=x -> true
    | (t, x::xs) -> t=x || memberof(t,xs)

(* val remove : 'a * 'a list -> 'a list when 'a : equality *)
let rec remove ((k:'a),(l:'a list)) = 
    match l with
    | [] -> []
    | x::xs when x=k -> xs
    | x::xs ->x::(remove(k,xs))

(* End of question 2 *) (* Do not edit this line *)

(* Question 3 *) (* Do not edit this line *)

(* val isolate : l:'a list -> 'a list when 'a : equality *)
let rec isolate (l:'a list) = 
    match l with
    | [] -> []
    | x::xs ->
        if memberof(x,xs)
        then
            let xs = remove (x,l)
            isolate xs
        else isolate xs

(* End of question 3 *) (* Do not edit this line *)

(* Question 4 *) (* Do not edit this line *)

(* val common : 'a list * 'a list -> 'a list when 'a : equality *)
let rec common (k: 'a list, l:'a list) = 
    match ((k:'a list),(l:'a list)) with
    | (a, b) -> 
        if a=[] then []
        elif b=[] then []    
        elif List.append(isolate(a),isolate(b)) <> [] then List.append(isolate(a),isolate(b))
        else []
common([1.0;2.0;6.0;10.0],[5.0;6.0;10.0])

似乎&lt;&gt;已经修复了问题,但你对我的函数mean_diffs有任何建议吗?

1 个答案:

答案 0 :(得分:3)

由于这似乎是你正在开发一门课程而且它是基于之前的练习,所以代码转换为更多F#惯用语和递归函数的标准化格式,以便在到达{{3时更容易使用请参阅:curryingF# for fun and profit以及其他更高级的概念。

格式基本上是

let funXYZ list =
    let rec funXYZInner list acc =
        match list with
        | head :: tail ->
            let acc = (somefunc head) :: acc
            funXYZInner tail acc
        | [] -> acc
    funXYZInner list []

其中funXYZ是一个没有rec的公开函数名。我无法回想起源代码,但是如果你可以实现一个需要rec而不暴露rec的函数,那么它就会使代码更具可移植性。

基本概念是你拿一个列表并将列表分成headtail

head :: tail

然后你处理头部:

somefunc head

然后将结果累积到累加器

let acc = value :: acc
let acc = value + acc
let acc = acc + (value * value)

然后处理列表的其余部分,例如尾巴,通过累加器

funXYZInner tail acc

当输入列表与空

匹配时
| []

只返回累加器中累积的结果

acc

内部函数funXYZInner确实有Functions as First-Class Values (F#)并使用rec,即acc。这将有助于理解如何使用accumulator,这将使您在大型计算时不会耗尽内存。

您可能已经知道,对于match语句,您希望涵盖匹配变量的所有情况。这是因为tail calls,因此您会看到有关未涵盖所有案例的警告。如果你看到其中一个警告并且不知道你为什么会得到它,你需要修复它们,或者冒着意外的运行时错误或崩溃的风险。

虽然您提供的代码只能使用浮动类型列表,但将来要使其适用于更多类型,您需要了解algebraic data types

由于需要添加一些更基本的功能,例如reverse,并帮助显示进展,因为示例变得更加复杂。

事实上,示例建立在自己的基础上,并且在最后一个示例中存在特定错误,最好为示例提供一个更好的基础,以减轻首次学习递归函数时遇到的常见问题。

关于累加器,累加器可以保持不同类型,例如, floatlistint,并且在递归函数中可以使用多个累加器,例如numeratorAccdenominatorAcc。此外,通过拉出累加器值的计算,例如let acc = ...,当您使用更高级的函数时,您可以传递一个函数来替换该计算。

有一个LanguagePrimitives.GenericZero<^T> Type Function (F#)函数memberof不使用累加器。谓词是一个返回true或false的函数,一旦达到所需的值,就可以停止处理列表的其余部分。

同样值得注意的是,虽然某些功能可以调用先前定义的功能,但示例不会进行调用,以便他们可以一次性处理列表。当函数使用list调用其他函数时,每个函数都必须处理整个列表以返回结果。通过使用rec函数,有时可以通过对头进行多次计算来处理列表一次。但有时候这是不可能的。我并没有以某种方式最大化功能,而是给他们留下了一种为学习提供更多变化的方法。随意重写它们,这将导致predicate

您可能会对这些示例有更多疑问,因此请将其作为单独的SO问题而不是基于此问题。

所有代码

// val reverse : l:'a list -> 'a list
let reverse l =
    let rec reverseInner l acc =
        match l with
        | x::xs -> 
            let acc = x :: acc
            reverseInner xs acc
        | [] -> acc
    reverseInner l []

reverse [ 3.0; 2.0; 1.0 ]  // val it : float list = [1.0; 2.0; 3.0]    

// val length : l:'a list -> int
let length l =
    let rec lengthInner l acc =
        match l with
        | x::xs -> 
            let acc = acc + 1
            lengthInner xs acc
        | [] -> acc
    lengthInner l 0

length [ 3.0; 2.0; 1.0 ]  // val it : int = 3

// val sum : l:float list -> float
let sum l =
    let rec sumInner l acc =
        match l with
        | x::xs -> 
            let acc = acc + x
            sumInner xs acc
        | [] -> acc
    sumInner l 0.0

sum [ 1.0; 2.0; 3.0 ]  // val it : float = 6.0

// val square : l:float list -> float list
let square (l : float list) = 
    let rec squareInner l acc =
        match l with
        | x::xs -> 
            let acc = (x * x) :: acc
            squareInner xs acc
        | [] -> reverse acc
    squareInner l []

square [ 1.0; 2.0; 3.0 ]  // val it : float list = [1.0; 4.0; 9.0]

// val mean : l:float list -> float
let mean l = 
    let rec meanInner l sumacc lengthacc =
        match l with
        | x::xs -> 
            let sumacc = sumacc + x
            let lengthacc = lengthacc + 1.0
            meanInner xs sumacc lengthacc
        | [] -> sumacc / lengthacc
    meanInner l 0.0 0.0

mean([1.0;2.0;3.0]) // val it : float = 2.0

// val mean_diffs : l:float list -> float list
let meanDiff l = 
    let rec meanDiffInner l m acc =
        match l with
        | x::xs -> 
            let diff = (x - m)
            let acc = diff :: acc
            meanDiffInner xs m acc
        | [] -> reverse acc
    meanDiffInner l (mean l) []

meanDiff [ 1.0; 2.0; 3.0 ]  // val it : float list = [-1.0; 0.0; 1.0]


// From: https://en.wikipedia.org/wiki/Variance
// Suppose a population of numbers consists of 3, 4, 7, and 10. 
// The arithmetic mean of these numbers, often informally called the "average", is (3+4+7+10)÷4 = 6. 
// The variance of these four numbers is the average squared deviation from this average. 
// These deviations are (3–6) = –3, (4–6) = –2, (7–6) = 1, and (10–6) = 4. 
// Thus the variance of the four numbers is ((-3)^2 + (-2)^2 + (1)^2 + (4)^2) / 4 = 15/2 = 7.5

// val variance : l:float list -> float
let variance l = 
    let deviations = meanDiff l
    let rec varianceInner l numeratorAcc denomenatorAcc =
        match l with
        | devation::xs ->
            let numeratorAcc = numeratorAcc + (devation * devation) 
            let denomenatorAcc = denomenatorAcc + 1.0
            varianceInner xs numeratorAcc denomenatorAcc
        | [] -> numeratorAcc / denomenatorAcc
    varianceInner deviations 0.0 0.0

variance [ 1.0; 2.0; 3.0 ]          // val it : float = 0.6666666667
variance [ 3.0; 4.0; 7.0; 10.0 ]    // val it : float = 7.5


(* End of question 1 *) (* Do not edit this line. *)

(* Question 2 *) (* Do not edit this line. *)

// val memberof : l:'a list -> item:'a -> bool when 'a : equality
let memberof l item =
    let rec memberInner l item =
        match l with
        | x::xs -> 
            if x = item then
                true
            else 
                memberInner xs item
        | [] -> false
    memberInner l item

memberof [ 1.0; 2.0; 3.0 ] 0.0  // val it : bool = false
memberof [ 1.0; 2.0; 3.0 ] 1.0  // val it : bool = true
memberof [ 1.0; 2.0; 3.0 ] 2.0  // trueval it : bool = true
memberof [ 1.0; 2.0; 3.0 ] 3.0  // val it : bool = true
memberof [ 1.0; 2.0; 3.0 ] 4.0  // val it : bool = false

// val remove : l:'a list -> item:'a -> 'a list when 'a : equality
let remove l item =
    let rec removeInner l item acc =
        match l with
        | x::xs ->
            if x = item then
                removeInner xs item acc
            else
                let acc = x :: acc
                removeInner xs item acc
        | [] -> reverse acc
    removeInner l item []

remove [ 1.0; 2.0; 3.0 ] 0.0    // val it : float list = [1.0; 2.0; 3.0]
remove [ 1.0; 2.0; 3.0 ] 1.0    // val it : float list = [2.0; 3.0]
remove [ 1.0; 2.0; 3.0 ] 2.0    // val it : float list = [1.0; 3.0]
remove [ 1.0; 2.0; 3.0 ] 3.0    // val it : float list = [1.0; 2.0]
remove [ 1.0; 2.0; 3.0 ] 4.0    // val it : float list = [1.0; 2.0; 3.0]

(* End of question 2 *) (* Do not edit this line *)

(* Question 3 *) (* Do not edit this line *)

// val isolate : list:'a list -> 'a list when 'a : equality
let isolate list =
    let rec isolateInner searchList commonlist =
        match searchList with
        | x::xs ->
            if (memberof commonlist x) then
                isolateInner xs commonlist
            else
                let commonlist = (x :: commonlist)
                isolateInner xs commonlist
        | [] -> reverse commonlist
    isolateInner list []

isolate [ 1.0; 2.0; 3.0 ]                                    // val it : float list = [1.0; 2.0; 3.0]
isolate [ 1.0; 1.0; 2.0; 3.0 ]                               // val it : float list = [1.0; 2.0; 3.0]
isolate [ 1.0; 2.0; 2.0; 3.0 ]                               // val it : float list = [1.0; 2.0; 3.0]
isolate [ 1.0; 2.0; 3.0; 3.0 ]                               // val it : float list = [1.0; 2.0; 3.0]
isolate [ 3.0; 2.0; 1.0; 1.0; 2.0; 3.0; 2.0; 1.0; 1.0; 3.0]  // val it : float list = [3.0; 2.0; 1.0]

(* End of question 3 *) (* Do not edit this line *)

(* Question 4 *) (* Do not edit this line *)

// val common : a:'a list -> b:'a list -> 'a list when 'a : equality
let common a b = 
    let rec commonInner a b acc =
        match (a,b) with
        | (x::xs,b) ->
            if (memberof acc x) then
                commonInner xs b acc
            else
                let acc = x :: acc
                commonInner xs b acc
        | ([],y::ys) ->
            if (memberof acc y) then
                commonInner [] ys acc
            else
                let acc = y :: acc
                commonInner [] ys acc
        | ([],[]) -> reverse acc
    commonInner a b []

common [ 1.0; 2.0; 6.0; 10.0] [ 5.0; 6.0; 10.0 ]   // val it : float list = [1.0; 2.0; 6.0; 10.0; 5.0]