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