簡體   English   中英

如何在 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM