List 如何在haskell中使用特定索引在2D列表中添加数字
我是haskell的初学者,我试着在二维列表中添加一个数字,在haskell中有特定的索引,但我不知道怎么做 我举了一个例子:List 如何在haskell中使用特定索引在2D列表中添加数字,list,haskell,List,Haskell,我是haskell的初学者,我试着在二维列表中添加一个数字,在haskell中有特定的索引,但我不知道怎么做 我举了一个例子: [[],[],[]] 我想在索引1中加一个数字(3),如下所示 [[],[3],[]] 我试过这个 [array !! 1] ++ [[3]] 但是它不起作用正如您在迄今为止的尝试中可能已经注意到的,Haskell不像其他许多语言那样,因为它通常是不可变的,因此尝试更改值,特别是在这样的深度嵌套结构中,并不是一件容易的事情[array!!1]将为您提供一个嵌套列表
[[],[],[]]
我想在索引1中加一个数字(3),如下所示
[[],[3],[]]
我试过这个
[array !! 1] ++ [[3]]
但是它不起作用正如您在迄今为止的尝试中可能已经注意到的,Haskell不像其他许多语言那样,因为它通常是不可变的,因此尝试更改值,特别是在这样的深度嵌套结构中,并不是一件容易的事情
[array!!1]
将为您提供一个嵌套列表[[]]
,但这是不可变的,因此您对该结构所做的任何操作都不会反映在原始数组中,它将是一个单独的副本
(有一些特殊的环境,您可以在其中执行局部可变性,例如ST monad中的向量,但这是一个例外。)
对于您尝试执行的操作,您必须解构列表,使其达到可以轻松进行修改的程度,然后从(修改的)部分重建最终结构
splitAt
函数似乎可以帮助您实现这一点:它获取一个列表,并在您给定的索引处将其分成两部分
let array = [[],[],[]]
splitAt 1 array
我会给你
([[]], [[],[]])
这有助于您更接近所需的列表,即中间嵌套列表
让我们进行一次解构绑定,以便以后能够重建最终列表:
let array = [[],[],[]]
(beginning, end) = splitAt 1 array
接下来,您需要获得所需的子列表,它是结束
列表中的第一项:
desired = head end
现在您可以进行修改了--注意,这将生成一个新列表,它不会修改其中的列表:
desired' = 3:desired
现在我们需要把它放回end
列表中。不幸的是,结束
列表仍然是[[],[]]
的原始值,因此我们必须用我们的所需的“
替换此列表的开头,以使其正确:
end' = desired' : (tail end)
这会在开始处删除空的子列表,并将修改后的列表附加到其位置
现在只剩下将修改后的end'
与原始的开头重新组合:
in beginning ++ end'
制作整个片段:
let array = [[],[],[]]
(beginning, end) = splitAt 1 array
desired = head end
desired' = 3:desired
end' = desired' : (tail end)
in beginning ++ end'
或者,如果您在REPL中输入所有这些命令:
let array = [[],[],[]]
let (beginning, end) = splitAt 1 array
let desired = head end
let desired' = 3:desired
let end' = desired' : (tail end)
beginning ++ end'
正如我们所知,Haskell中的事物是不可变的。您要做的不是就地修改列表,而是通过分解列表结构、转换其中一个部分以及使用此更改的部分重新构造列表。其中提出了一种解构方式(通过splitAt
);我想再给你一个
Haskell中的列表定义如下:
data[]a=[]a:[a]
这表示“一个A
列表要么为空,要么是一个A
,后跟一个A
列表”(:)
读作“cons”表示“constructor”,您可以使用它创建非空列表
1:[]->[1]
1 : [2,3] -> [1,2,3]
1 : 2 : 3 : [] -> [1,2,3]
由于模式匹配,这是双向的。如果您有一个列表[1,2,3]
,将它与x:xs
匹配将把它的头1
绑定到名称x
,把它的尾[2,3]
绑定到xs
。如您所见,我们已将列表分解为最初用于创建它的两部分。然后,我们可以在重新整理列表之前对这些部分进行操作:
λ>设x:xs=[1,2,3]
λ> 设y=x-5
λ> y:xs
[-4,2,3]
因此,在您的情况下,我们可以将初始列表与x:y:z:[]
,计算w=y++[3]
,并构建新列表:
λ>设x:y:z:[]=[],[],[]
λ> 设w=y++[3]
λ> [x,w,z]
[[],[3],[]]
但这不是很好的扩展性,也不能解决您提出的问题(“使用特定索引”)。如果稍后我们要更改列表的第千项,该怎么办?我不太喜欢搭配那么多的衣服。幸运的是,我们对listxs中的list indexn
有一些了解,它是listx:xs中的indexn+1
。因此,我们可以递归,沿着列表移动一步,并在每一步中递减索引:
foo::Int->[[Int]]->[[Int]]
foo0(x:xs)=TODO——索引0是x。我们到了;在这里,我们在重新构造列表之前先与[3]连接。
foo n(x:xs)=x:foo(n-1)xs
foon n[]=TODO——由您决定如何处理无效索引。考虑函数误差。
假设您是在索引0上操作,那么您自己实现这三个步骤中的第一个。确保您理解第二节中的递归调用。然后继续读下去
现在,这是可行的。虽然它对一个特定类型的列表中的指定项执行预先确定的计算,但它并不是那么有用。是概括的时候了。我们需要的是以下类型签名的函数:
bar::(a->a)->Int->[a]->[a]
其中bar f n xs
将转换f
应用于列表xs
中索引n
处的值。有了它,我们可以实现以前的功能:
foo n xs=bar(++[3])n xs
foo=bar(++[3])——或者,部分应用
信不信由你,将你已经编写的foo
更改为更有用的条
是一项非常简单的任务。试试看