例如,給定一個元組(2, 3)作為引數,它應該回傳[(0,0), (0,1), (0,2), (1,0), (1,1), (1,2)],盡管它可以是任何順序。
這是我想出的代碼,但是它沒有回傳正確的結果,有人可以提供更好的解決方案或想法嗎?
getAll :: (Int, Int) —> [(Int,Int)]
getAll (x,y)
| x == 0 && y == 0 = [(0,0)]
| x == 1 && y == 1 = [(1,1)]
| 0 1 < x && 0 1 < y
= (0,0) : getAll (x 1, y 1)
更新:
getAll :: (Int, Int) -> [(Int,Int)]
getAll (x,y) = case (x,y) of
(0,0) -> [(0,0)]
(1,1) -> [(0,0)]
(0, y) -> (0, y-1) : getAll (0, y-1)
(x, 0) -> (x-1, 0) : getAll (x-1, 0)
我嘗試將其計算為兩種情況,但是我有一個問題,例如當我輸入時(0,3),它會輸出[(0,2), (0,1), (0,0), (0,0)]. 我知道為什么這會輸出(0,0)兩次,但我不知道如何解決,你能不能看看我的代碼,看看問題出在哪里?
uj5u.com熱心網友回復:
我能想到的一種干凈的實作方法是使用串列推導式:
getAll :: (Int, Int) -> [(Int,Int)]
getAll (x, y) = [(a, b) | a <- [0..x-1], b <- [0..y-1]]
如果你必須用遞回來解決它:
getAll :: (Int, Int) -> [(Int,Int)]
getAll (x, y) = go 0 0 where
go x' y'
| x' == x = []
| y' == y = go (x' 1) 0
| otherwise = (x', y') : go x' (y' 1)
uj5u.com熱心網友回復:
你寫
getAll (x,y)
| x == 0 && y == 0 = [(0,0)]
到目前為止,一切都很好。
| x == 1 && y == 1 = [(1,1)]
這已經是錯誤的。它應該產生[(0,0), (0,1), (1,0), (1,1)].
從描述中可以清楚地看出這一點。這兩個Ints 定義了兩個范圍,
[ (i,j) | i <- [0..x-1], j <- [0..y-1]]
這被稱為串列推導式。事實上,這段代碼就是您在函式中所需要的全部代碼。也不需要測驗任何情況。它會起作用。
但是這是什么意思?就像它看起來的那樣,在視覺上,它創建了一個串列,其中包含i來自0upto的sx和j來自0upto的s的所有配對y。
有很多其他方法可以對此進行編碼,但最終還是要列舉矩形中的單元格
j 0 1 2 ........ y-1
i
0 * * * * * * * *
1 * * * * * * * *
2 * * * * * * * *
. * * * * * * * *
. * * * * * * * *
x-1 * * * * * * * *
它可以用偽代碼表示為兩個嵌套回圈:
for i in 0 .. x-1 :
for j in 0 .. y-1 :
produce (i,j)
嵌套回圈可以展開為一系列回圈:
for i=0 and j in 0 .. y-1 : produce (i,j)
for i=1 and j in 0 .. y-1 : produce (i,j)
for i=2 and j in 0 .. y-1 : produce (i,j)
..............
for i=x-1 and j in 0 .. y-1 : produce (i,j)
以上可以表示為一個回圈,它j在提高i值的同時達到限制后重置值......這就是其他答案中的遞回版本正在做的事情,一一生成對并將它們放入輸出串列中:。嵌套回圈通常在 Haskell 中編碼為concatMaps,一一(逐行)生成串列并將它們與 而不是:....
有很多方法可以對此進行編碼。但最終它是關于追蹤那個正方形(長方形,無論如何):
concat -- concat
[ [ (i , j) -- [ map (i ,)
| j <- [0..y-1] ] -- [0..y-1]
| i <- [0..x-1] ] -- | i <- [0..x-1] ]
==
[ (0,j) | j <- [0..y-1] ]
[ (1,j) | j <- [0..y-1] ]
[ (2,j) | j <- [0..y-1] ]
............
[ (x-1,j) | j <- [0..y-1] ]
你可以看到它是如何真正相同的。
我們甚至可以通過 對角線來追蹤它。
uj5u.com熱心網友回復:
你的遞回函式最終應該終止,但你只在(0, 0)或 的情況下終止(1, 1)。如果x和y大于 1,則進行遞回呼叫,同時增加xand y,因此永遠不會終止。其他情況將被忽略,因此(0, 1)例如會引發錯誤。
您可以做的是使用以 開頭的輔助函式(0, 0),并且每次增加y坐標,直到該坐標大于或等于上限。在這種情況下,我們使用y坐標零進行遞回呼叫,并增加x坐標。喜歡:
getAll :: (Int, Int) -> [(Int, Int)]
getAll (xn, yn) = go (0, 0)
where go (x, y)
| x >= xn = [] -- terminate, we reached the end
| y >= yn = go (x 1, 0) -- increment x, set y to 0
| otherwise = (x, y) : go (x, y 1) -- yield and increment y
由于專案可以按任何順序回傳,另一種策略是遞減值,從而將y(或x)設定回初始y(或x)并遞減另一個坐標(y或x),直到兩者都為零,因此:
getAll :: (Int, Int) -> [(Int, Int)]
getAll t@(_, yn) = go t
where go (x, y)
| x < 0 = []
| y < 0 = go (x-1, yn)
| otherwise = (x, y) : go (x, y-1)
但是,您可以使用串列推導式或串列Applicative實體以更緊湊的方式生成此結果。例如:
getAll :: (Int, Int) -> [(Int, Int)]
getAll (x, y) = (,) <$> [0 .. x-1] <*> [0 .. y-1]
這里f <$> xs <*> ys將構建一個串列,我們在其中呼叫f來自xs和 的每個專案組合ys。因此,我們將對于元素from和每個元素from 的每個組合,呼叫它是更規范的形式xixsyiys(,) xi yi(xi, yi)
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/316866.html
