Matrix 将两个Int列表添加到一起#

Matrix 将两个Int列表添加到一起#,matrix,vector,f#,Matrix,Vector,F#,我正在做作业,问题是我们得到两个相同大小的整数列表,然后把数字加在一起。示例如下 vecadd [1;2;3] [4;5;6];; would return [5;7;9] 我是新手,我需要保持我的代码非常简单,这样我就可以从中学习。到目前为止,我有这个。(不工作) 他们两个都不工作,我将非常感激能得到的任何帮助 您可以使用List.map2同时映射两个列表(请参阅) 它两两遍历两个列表,您可以给它一个函数(List.map2的第一个参数)来应用于列表中的每一对元素。这将生成新的列表

我正在做作业,问题是我们得到两个相同大小的整数列表,然后把数字加在一起。示例如下

    vecadd [1;2;3] [4;5;6];; would return [5;7;9]
我是新手,我需要保持我的代码非常简单,这样我就可以从中学习。到目前为止,我有这个。(不工作)


他们两个都不工作,我将非常感激能得到的任何帮助

您可以使用List.map2同时映射两个列表(请参阅) 它两两遍历两个列表,您可以给它一个函数(List.map2的第一个参数)来应用于列表中的每一对元素。这将生成新的列表

 let a = [1;2;3]
 let b = [4;5;6]

 let vecadd  = List.map2 (+)

 let result  = vecadd a b
 printfn "%A" result
如果你不想自己做更多类似的工作

let a = [1;2;3]
let b = [4;5;6]

let vecadd l1 l2 = 
    let rec step l1 l2 acc = 
        match l1, l2 with
            | [],  [] -> acc
            | [], _ | _, [] -> failwithf "one list is bigger than the other"
            | h1 :: t1, h2 :: t2 -> step t1 t2 (List.append acc [(h1 + h2)])
    step l1 l2 []
let result  = vecadd a b
printfn "%A" result
step函数是一个递归函数,它使用两个列表和一个累加器来携带结果

  • 在最后一个match语句中,它做了三件事
    • 将两个列表的标题相加
    • 将结果添加到累加器中
    • 使用新的累加器和列表的尾部递归地调用自己
  • 当剩余列表为空时,第一个匹配返回累加器
  • 当一个列表比另一个列表长时,第二个匹配返回错误。 当剩余列表为空时,将返回累加器作为结果

调用
步骤l1 l2[]
以提供的两个列表和一个空累加器开始。首先,您关于修改第一个列表而不是返回新列表的想法是错误的。变异(即在适当的位置修改数据)是今天出现bug的首要原因(以前是
goto
,但现在已经被禁止了很长时间)。让每一次操作都产生一个新的数据,而不是修改现有的数据,这是非常非常安全的。在某些情况下,它可能会表现得更出色,这与直觉相反(见下文)

第二,你尝试的方式是,你没有做你认为你在做的事情。双冒号并不意味着“修改第一项”。它的意思是“在前面附加一个项目”。例如:

let a = [1; 2; 3]
let b = 4 :: a    // b = [4; 1; 2; 3]
let c = 5 :: b    // c = [5; 4; 1; 2; 3]
这就是列表的实际构建方式:从一个空列表开始,并在其前面添加项目。您使用的
[1;2;3]
语法只是一种语法糖。也就是说,
[1;2;3]==1::2::3::[]

你会问,我该如何修改列表?答案是,你没有!F#列表是不可变的数据结构。一旦创建了列表,就不能对其进行修改

这种不变性允许进行有趣的优化。再看一看我上面发布的示例,其中有三个列表
a
b
,和
c
。你认为这三个列表占用了多少个内存单元?第一个列表有3个项目,第二个是-4,第三个是-5,所以占用的内存总量必须是12,对吗?错了!这三个列表占用的内存总量实际上只有5个单元。这是因为列表
b
不是长度为4的内存块,而是与指向列表
a
的指针配对的数字
4
。数字
4
称为列表的“头”,指针称为其“尾”。类似地,列表
c
由一个数字
5
(它的“头”)和一个指向列表
b
的指针组成,后者是它的“尾”

如果列表不是一成不变的,就不能这样组织它们:如果有人修改我的尾巴呢?每次都必须复制列表(谷歌“防御性复制”)

因此,处理列表的唯一方法是返回一个新列表。您试图做的事情可以这样描述:如果输入列表为空,则结果为空列表;否则,结果是尾部与头部之和的总和。你可以用F#几乎一字不差地写下来:

let rec add a b =
    match a, b with
    | [], [] -> []   // sum of two empty lists is an empty list
    | a::atail, b::btail -> (a + b) :: (add atail btail)  // sum of non-empty lists is sum of their tails prepended with sum of their heads

请注意,这个程序是不完整的:它没有指定当一个输入为空而另一个为空时结果应该是什么。编译器将生成有关此的警告。我将把这个解决方案留给读者作为练习。

我这样做是为了跨越两个列表(将具有相同索引的项目相乘):

let items=[1I..50_000I]
让另一个=[1I..50_000I]
让rec穿过a b=
让rec交叉_内部=函数
|r,[],[]->r
|r,[],t->r@t
|r,t,[]->r@t
|r,head::t1,head2::t2->cross_internal(r@[head*head2],t1,t2)
内部交叉([],a,b)
让结果=交叉项目
结果|>printf“%A,”
注意:不是真正的性能。每个步骤都有列表对象创建,这很可怕。理想情况下,内部函数
cross\u internal
必须创建一个可变列表并不断更新


注2:我的范围最初较大,使用
bigint
(因此
50_000
中的
I
后缀)但随后将上面的示例代码减少到仅50500个元素。

为什么不返回一个新列表?使用并返回一个新集合似乎是最惯用的方法me@FyodorSoikin作业没有指定我是否应该返回新列表。通常我会选择安全的方式,但在这种情况下,我想这并不重要。很抱歉造成混淆。“安全方式”和“修改现有列表”是不兼容的概念。非常有用且简单,但我正在寻找更像匹配语句或递归函数的内容。我需要坚持我们在课堂上学习的内容。你可以自己提供一个
map2
函数
let map2 f xs ys=let rec aux acc=function x::xs,y::ys->aux(f x y::acc)(xs,ys)|->List.rev acc in aux[](xs,ys)
这不是尾部递归对吗?它需要将所有的
(a+b)
保存在内存(堆栈)中,然后才能生成最终列表。如果这是正确的,那么我们不能添加包含5m个元素的列表,这是正确的,这个示例不是尾部递归的。
let a = [1; 2; 3]
let b = 4 :: a    // b = [4; 1; 2; 3]
let c = 5 :: b    // c = [5; 4; 1; 2; 3]
let rec add a b =
    match a, b with
    | [], [] -> []   // sum of two empty lists is an empty list
    | a::atail, b::btail -> (a + b) :: (add atail btail)  // sum of non-empty lists is sum of their tails prepended with sum of their heads