对 F# 中 List.mapi 的行为感到困惑
我正在 F# 中构建一些方程,在处理我的多项式类时,我使用 List.mapi 发现了一些奇怪的行为
基本上,每个多项式都有一个数组,因此 3*x^2 + 5*x + 6
将是数组中的 [|6, 5, 3|]
,因此,在添加多项式时,如果一个数组比另一个数组长,那么我只需将额外的元素附加到结果中,这就是我遇到问题的地方。
稍后我想将其概括为不总是使用 float
,但这将是在我进行更多工作之后。
所以,问题是我期望 List.mapi
返回一个 List
而不是单个元素,但是,为了将列表放在一起,我必须将 [ ]
围绕我对 mapi
的使用,我很好奇为什么会出现这种情况。
这比我预期的更复杂,我认为我应该能够告诉它从某个索引开始创建一个新的 List
,但我找不到任何函数。
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]
I am building some equations in F#, and when working on my polynomial class I found some odd behavior using List.mapi
Basically, each polynomial has an array, so 3*x^2 + 5*x + 6
would be [|6, 5, 3|]
in the array, so, when adding polynomials, if one array is longer than the other, then I just need to append the extra elements to the result, and that is where I ran into a problem.
Later I want to generalize it to not always use a float
, but that will be after I get more working.
So, the problem is that I expected List.mapi
to return a List
not individual elements, but, in order to put the lists together I had to put []
around my use of mapi
, and I am curious why that is the case.
This is more complicated than I expected, I thought I should be able to just tell it to make a new List
starting at a certain index, but I can't find any function for that.
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]
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我认为在这种情况下,使用列表和递归的直接实现会更简单。 Polynomial 类的替代实现可能大致如下所示:
值得注意的是,您实际上并不需要类中的可变字段,因为
+
运算符创建并返回该类型的新实例,因此该类型是完全不可变的(正如您在 F# 中通常希望的那样)。add
函数的好处是,在处理完两个列表中可用的所有元素后,您可以简单地将非空列表的尾部作为其余元素返回。如果您想使用数组实现相同的功能,那么最好使用简单的
for
循环(因为数组原则上是命令式的,通常的命令式模式通常是处理问题的最佳选择)与他们)。但是,我不认为有任何特别的理由选择数组(也许是性能,但这必须在开发过程中稍后进行评估)。正如 Pavel 指出的,
::
运算符将单个元素附加到列表的前面(请参阅上面的add
函数,它演示了这一点)。您可以使用连接列表的@
或使用Array.concat
(连接数组序列)来编写您想要的内容。使用高阶函数和数组的实现也是可能的 - 我能想到的最佳版本如下所示:
这使用切片(例如
a.[0 .. l]< /code>) 为您提供数组的一部分 - 您可以使用它们来获取两个数组都有元素的部分以及较长数组的剩余部分。
I think that a straightforward implementation using lists and recursion would be simpler in this case. An alternative implementation of the
Polynomial
class might look roughly like this:It is worth noting that you don't really need mutable field in the class, since the
+
operator creates and returns a new instance of the type, so the type is fully immutable (as you'd usually want in F#).The nice thing in the
add
function is that after processing all elements that are available in both lists, you can simply return the tail of the non-empty list as the rest.If you wanted to implement the same functionality using arrays, then it may be better to use a simple
for
loop (since arrays are, in principle, imperative, the usual imperative patterns are usually the best option for dealing with them). However, I don't think there is any particular reason for preferring arrays (maybe performance, but that would have to be evaluated later during the development).As Pavel points out,
::
operator appends a single element to the front of a list (see theadd
function above, which demonstrates that). You could write what you wanted using@
which concatenates lists, or usingArray.concat
(which concatenates a sequence of arrays).An implementation using higher-order functions and arrays is also possible - the best version I can come up with would look like this:
This uses slices (e.g.
a.[0 .. l]
) which give you a part of an array - you can use these to take the parts where both arrays have elements and the remaining part of the longer array.我认为您误解了运算符
::
的作用。它不用于连接两个列表。它用于将单个元素添加到列表中。因此,它的类型是:在您的情况下,您将 ret 作为第一个参数,而 ret 本身就是一个浮点列表。因此,它期望第二个参数的类型为
float list list
- 这就是为什么你需要向第二个参数添加额外的[]
来使其编译 - 并且这也将是您的运算符+
的结果类型,这可能不是您想要的。您可以使用List.concat连接两个(或更多)列表,但这效率很低。在您的示例中,我根本看不到使用列表的意义 - 所有这些转换回来 &未来将是昂贵的。对于数组,可以使用Array.append,效果更好。
顺便说一句,您根本不清楚代码中
mapi
的用途。它与map
完全相同,除了索引参数之外,但您没有使用它,并且您的映射是恒等函数,因此它实际上是一个无操作。是关于什么的?I think you're misunderstanding what operator
::
does. It's not used to concatenate two lists. It's used to prepend a single element to the list. Consequently, it's type is:In your case, you're giving
ret
as a first argument, andret
is itself afloat list
. Consequently, it expects the second argument to be of typefloat list list
- hence why you need to add an extra[]
to the second argument to make it to compile - and that will also be the result type of your operator+
, which is probably not what you want.You can use
List.concat
to concatenate two (or more) lists, but that is inefficient. In your example, I don't see the point of using lists at all - all this converting back & forth is going to be costly. For arrays, you can useArray.append
, which is better.By the way, it's not clear what is the purpose of
mapi
in your code at all. It's exactly the same asmap
, except for the index argument, but you're not using it, and your mapping is the identity function, so it's effectively a no-op. What's it about?