代码之家  ›  专栏  ›  技术社区  ›  James Black

混淆F中List.mapi的行为#

  •  2
  • James Black  · 技术社区  · 16 年前

    我正在F#中建立一些方程,在处理多项式类时,我发现使用List.mapi有一些奇怪的行为

    3*x^2 + 5*x + 6 会是 [|6, 5, 3|] 在数组中,所以,当添加多项式时,如果一个数组比另一个长,那么我只需要在结果中添加额外的元素,这就是我遇到问题的地方。

    后来我想把它推广到不总是使用 float ,但那要等我多工作之后。

    所以,问题是我预料到的 List.mapi List 不是单个元素,但是,为了把列表放在一起,我必须 [] mapi ,我很好奇为什么会这样。

    这比我想象的要复杂,我想我应该可以告诉它做一个新的 列表 从某个索引开始,但我找不到任何函数。

    type Polynomial() =
        let mutable coefficients:float [] = Array.empty
        member self.Coefficients with get() = coefficients
        static member (+) (v1:Polynomial, v2:Polynomial) =
            let ret = List.map2(fun c p -> c + p) (List.ofArray v1.Coefficients) (List.ofArray v2.Coefficients)
            let a = List.mapi(fun i x -> x)
            match v1.Coefficients.Length - v2.Coefficients.Length with
                | x when x < 0 ->
                    ret :: [((List.ofArray v1.Coefficients) |> a)]
                | x when x > 0 ->
                    ret :: [((List.ofArray v2.Coefficients) |> a)]
                | _ -> [ret]
    
    2 回复  |  直到 16 年前
        1
  •  5
  •   Tomas Petricek    16 年前

    我认为在这种情况下,使用列表和递归的直接实现会更简单。另一种实现 Polynomial 类可能大致如下所示:

    // The type is immutable and takes initial list as constructor argument
    type Polynomial(coeffs:float list) = 
      // Local recursive function implementing the addition using lists
      let rec add l1 l2 = 
        match l1, l2 with
        | x::xs, y::ys -> (x+y) :: (add xs ys)
        | rest, [] | [], rest -> rest
    
      member self.Coefficients = coeffs
    
      static member (+) (v1:Polynomial, v2:Polynomial) = 
        // Add lists using local function
        let newList = add v1.Coefficients v2.Coefficients
        // Wrap result into new polynomial
        Polynomial(newList)
    

    +

    世界上最美好的事情 add 函数是在处理了 两个列表


    如果希望使用数组实现相同的功能,那么最好使用简单的 for 循环(由于数组原则上是命令式的,所以通常的命令式模式通常是处理它们的最佳选择)。但是,我不认为有什么特别的原因需要选择阵列(可能是性能,但这需要在开发过程中稍后进行评估)。

    :: 运算符将单个元素附加到列表的前面(请参见 添加 上面的函数,这表明)。你可以用它写你想写的东西 @ 连接列表,或使用 Array.concat

    使用高阶函数和数组的实现也是可能的—我能想到的最佳版本如下所示:

    let add (a1:_[]) (a2:_[]) =
      // Add parts where both arrays have elements
      let l = min a1.Length a2.Length
      let both = Array.map2 (+) a1.[0 .. l-1] a2.[0 .. l-1]  
      // Take the rest of the longer array
      let rest = 
        if a1.Length > a2.Length 
        then a1.[l .. a1.Length - 1] 
        else a2.[l .. a2.Length - 1]
      // Concatenate them
      Array.concat [ both; rest ]
    
    add [| 6; 5; 3 |] [| 7 |]  
    

    它使用 (例如。 a.[0 .. l] )这将为您提供一个数组的一部分—您可以使用这些来获取两个数组都有元素的部分以及较长数组的其余部分。

        2
  •  2
  •   Pavel Minaev    16 年前

    我想你误解了什么接线员 :: 单一元素 加入名单。因此,其类型为:

    'a -> 'a list -> 'a list
    

    在你的情况下,你给 ret 作为第一个论点 它本身就是一个 float list float list list -因此,您需要添加一个额外的 [] 对第二个参数进行编译-这也是运算符的结果类型 +

    你可以用 List.concat 连接两个(或多个)列表,但效率低下。在您的示例中,我完全看不到使用列表的意义—所有这些都转换回&福斯将是昂贵的。对于数组,可以使用 Array.append ,哪个更好。

    顺便说一下,现在还不清楚 mapi 在你的代码里。完全一样 map

    推荐文章