[英]How to count the number of 1's surrounding a given element in a 2D list with Haskell?
[英]how to add a number in a 2D list with specific index in haskell
我是 haskell 的初學者,我試圖在 haskell 中的特定索引的二維列表中添加一個數字,但我不知道該怎么做
例如我有這個:
[[],[],[]]
我想像這樣在索引 1 中放一個數字 (3)
[[],[3],[]]
我試過這個
[array !! 1] ++ [[3]]
但它不起作用
到目前為止,您可能已經注意到,Haskell 與許多其他語言不同,因為它通常是不可變的,因此嘗試更改值,尤其是在像這樣的深度嵌套結構中,並不是最簡單的事情。 [array !! 1]
[array !! 1]
會給你一個嵌套列表[[]]
但這不是可變的,所以你對這個結構所做的任何操作都不會反映在原始array
中,它將是一個單獨的副本。
(有專門的環境可以在其中進行局部可變性,例如 ST monad 中的 Vectors,但這些是一個例外。)
對於您要嘗試做的事情,您必須解構列表以使其達到可以輕松進行修改的程度,然后從(修改后的)部分重建最終結構。
splitAt
function 看起來可以幫助您解決這個問題:它需要一個列表並在您提供的索引處將其分成兩部分。
let array = [[],[],[]]
splitAt 1 array
會給你
([[]], [[],[]])
這可以幫助您更接近您想要的列表,即中間嵌套列表。
讓我們做一個解構綁定,以便以后能夠重建您的最終列表:
let array = [[],[],[]]
(beginning, end) = splitAt 1 array
接下來,您需要獲得所需的子列表,這是end
列表中的第一項:
desired = head end
現在您可以進行修改了——注意,這將生成一個新列表,它不會修改那里的列表:
desired' = 3:desired
現在我們需要把它放回end
列表。 不幸的是, end
列表仍然是[[],[]]
的原始值,所以我們必須用我們desired'
替換它的頭部以使其正確:
end' = desired' : (tail end)
這將刪除開頭的空子列表並將修改后的列表附加在其位置。
現在剩下的就是將修改后的end'
與原始beginning
重新組合:
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
。 如您所見,我們已將列表分解為最初用於創建它的兩個部分。 然后,我們可以在將列表重新組合在一起之前對這些部分進行操作:
λ> let x : xs = [1,2,3]
λ> let y = x - 5
λ> y : xs
[-4,2,3]
因此,在您的情況下,我們可以將初始列表與x: y: z: []
進行匹配,計算w = y ++ [3]
並構造我們的新列表:
λ> let x : y : z : [] = [[],[],[]]
λ> let w = y ++ [3]
λ> [x,w,z]
[[],[3],[]]
但這不是很可擴展,並且不能解決您提出的問題(“具有特定索引”)。 如果稍后我們想要更改列表的第 100 項怎么辦? 我不太熱衷於匹配那么多件。 幸運的是,我們對列表有所了解——列表 xs 中的索引n
是列表x:xs
xs
的索引n+1
。 所以我們可以遞歸,沿着列表移動一步,並在每一步遞減我們的索引:
foo :: Int -> [[Int]] -> [[Int]]
foo 0 (x:xs) = TODO -- Index 0 is x. We have arrived; here, we concatenate with [3] before restructuring the list.
foo n (x:xs) = x : foo (n-1) xs
foo n [] = TODO -- Up to you how you would like to handle invalid indices. Consider the function error.
假設您在索引零上操作,您自己實現這三個中的第一個。 確保您理解第二個中的遞歸調用。 然后繼續閱讀。
現在,這行得通。 不過,它並不是那么有用——它對一種特定類型列表中的指定項目執行預定計算。 是時候概括一下了。 我們想要的是以下類型簽名的 function:
bar :: (a -> a) -> Int -> [a] -> [a]
其中bar fn xs
將變換f
應用於列表xs
中索引n
處的值。 有了這個,我們可以實現之前的 function:
foo n xs = bar (++[3]) n xs
foo = bar (++[3]) -- Alternatively, with partial application
不管你信不信,把你已經寫的foo
改成更有用的bar
是一項非常簡單的任務。 試試看!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.