Collections 对F中List.mapi的行为感到困惑#

Collections 对F中List.mapi的行为感到困惑#,collections,f#,functional-programming,Collections,F#,Functional Programming,我正在F#中建立一些方程,在处理多项式类时,我发现使用List.mapi有一些奇怪的行为 基本上,每个多项式都有一个数组,因此3*x^2+5*x+6将是数组中的[| 6,5,3 |],因此,在添加多项式时,如果一个数组比另一个数组长,那么我只需要在结果中附加额外的元素,这就是我遇到问题的地方 稍后,我想将其推广为不总是使用浮点,但这将在我完成更多工作之后进行 所以,问题是我希望List.mapi返回的不是单个元素的List,但是为了将列表放在一起,我必须在使用mapi的周围放置[],我很好奇为什

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

基本上,每个多项式都有一个数组,因此
3*x^2+5*x+6
将是数组中的
[| 6,5,3 |]
,因此,在添加多项式时,如果一个数组比另一个数组长,那么我只需要在结果中附加额外的元素,这就是我遇到问题的地方

稍后,我想将其推广为不总是使用
浮点
,但这将在我完成更多工作之后进行

所以,问题是我希望
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]
type多项式()=
让可变系数:float[]=Array.empty
具有get()的成员自.系数=系数
静态成员(+)(v1:多项式,v2:多项式)=
让ret=List.map2(乐趣cp->c+p)(List.ofArray v1.系数)(List.ofArray v2.系数)
设a=List.mapi(funix->x)
将v1.coverties.Length-v2.coverties.Length与匹配
|当x<0->
ret::[((数组v1.系数列表)|>a)]
|当x>0->
ret::[((数组v2.系数列表)|>a)]
|[ret]

我认为在这种情况下,使用列表和递归的简单实现会更简单。
多项式
类的替代实现大致如下:

// 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)
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 |]  
值得注意的是,类中并不真正需要可变字段,因为
+
操作符创建并返回该类型的新实例,因此该类型是完全不可变的(正如您通常希望在F#中所做的那样)

add
函数的好处是,在处理了两个列表中可用的所有元素之后,您可以简单地返回非空列表的尾部作为其余部分


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

正如Pavel指出的,
操作符将单个元素附加到列表的前面(请参阅上面的
add
函数,该函数演示了这一点)。您可以使用连接列表的
@
或使用连接数组序列的
Array.concat
编写所需内容

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

// 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)
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]
),它为您提供了数组的一部分-您可以使用这些片来获取两个数组都有元素的部分以及较长数组的其余部分。

我想您误解了运算符
的作用。它不用于连接两个列表。它用于将单个元素前置到列表中。因此,其类型为:

'a -> 'a list -> 'a list
在您的例子中,您将
ret
作为第一个参数,而
ret
本身就是一个
float list
。因此,它期望第二个参数的类型为
浮点列表
——因此,您需要向第二个参数添加额外的
[]
,以使其能够编译——这也将是运算符
+
的结果类型,这可能不是您想要的

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


顺便说一句,在你的代码中,
mapi
的作用到底是什么还不清楚。除了index参数外,它与
map
完全相同,但您没有使用它,而且您的映射是标识函数,因此它实际上是一个不可操作的函数。它是关于什么的?

作为旁注-您不需要在get()中使用
,如果您只编写
成员self.coverties=coverties
,这将意味着。只有当您还需要
设置时才需要它
@Pavel Minaev-谢谢。我还没有决定是否需要一个set(),所以我只是把它放进去以防万一。:)我还没有使用索引,但mapi的目的是跳过已经添加在一起的任何内容,因此我只将这些元素包含在高于两个索引中较小的索引中。如果要跳过前n个项目,请查看
Seq.skip
。谢谢。我在Java中有很多函数,我正在尝试转移到F#,这不是一个简单的过程,但是,我需要做一个更正,add函数丢失了,因为(+)操作符是静态的。我刚把它放到(+)操作符中。更新得不错。我认为您可以使用
Array.append
而不是
Array.concat
更简洁、更快速(它特别需要2个数组参数,而不是数组的
seq
)。此外,对于数组切片,如果不指定上限,切片将位于数组末尾。这让您可以把它写得稍微短一点:
(如果a1.Length>a2.Length,那么a1-else a2)[l..]
@Pavel:谢谢您的有用补充!