[英]How can I make my simple Haskell function recursive?
這是我的功能:
rotatebyone :: [Int] -> [Int]
rotatebyone [] = []
rotatebyone (x:xs) = ((tail (x:xs)) ++ (take 1 (x:xs)))
rotatebyn :: Int -> [Int] -> [Int]
rotatebyn n [] = []
rotatebyn 1 (x:xs) = rotatebyone (x:xs)
rotatebyn 2 (x:xs) = (rotatebyone (rotatebyone (x:xs)))
我希望我的功能重復n次。 我猜您是通過監護人或“ while”運算符來執行此操作的,但是我不知道該怎么做。 目標是將整數列表的元素旋轉n次。
這是我失敗的方法:
rota :: Int -> [Int] -> [Int]
rota n (x:xs) = rotatebyone (x:xs)
where
rota n
| n > 1 = rota (n - 1)
| otherwise = rotatebyone (x:xs)
您不應該通過旋轉一次並重復多次來旋轉多個位置。 讓我們看一下單次輪換的簡潔版本,了解原因:
rotateLeft1 :: [a] -> [a]
rotateLeft1 [] = []
rotateLeft1 (x:xs) = xs ++ [x]
問題在於, p ++ q
在p
的長度上采用線性時間。 您為每個旋轉步驟執行一次此操作,這會浪費大量時間。 讓我們通過一個示例來不同地看待它。
rotateLeft 3 [1,2,3,4,5,6,7,8]
-- split the list
([1,2,3], [4,5,6,7,8])
-- swap the pieces
([4,5,6,7,8], [1,2,3])
-- Append
[4,5,6,7,8,1,2,3]
還剩下一個棘手的問題:如果我將列表旋轉大於其長度的數字怎么辦? 我讓你自己去做。
讓我們首先清理rotatebyone
函數:
rotatebyone :: [Int] -> [Int]
rotatebyone [] = []
rotatebyone (x:xs) = ((tail (x:xs)) ++ (take 1 (x:xs)))
在這里,您將tail
(x:xs)
但這很奇怪,因為我們已經有了一條尾標: xs
。 因此我們可以將tail (x:xs)
替換為xs
。
take 1 (x:xs)
。 這將以列表的開頭構造一個列表,因此[x]
。 因此,我們可以清理代碼以:
rotatebyone :: [Int] -> [Int]
rotatebyone [] = []
rotatebyone (x:xs) = xs ++ [x]
此外,您的函數僅適用於Int
類型的列表(它具有簽名[Int] -> [Int]
)。 但是,無論這些列表包含什么元素,我們都可以將列表旋轉一個,因此可以將其寫為:
rotatebyone :: [a] -> [a]
rotatebyone [] = []
rotatebyone (x:xs) = xs ++ [x]
現在,我們可以尋找一種旋轉n
次的方法:
rotatebyn :: Int ->[a] -> [a]
rotatebyn ...
如果我們希望在零位上旋轉,則列表保持不變,因此作為基本案例,我們可以編寫:
rotatebyn 0 l = l
如果我們要旋轉一個或多個位置,可以通過rotatebyone
函數旋轉一個位置,然后再將此結果旋轉n-1
次:
rotatebyn n l = rotatebyn (n-1) (rotateone n)
或這些在一起:
rotatebyn :: Int ->[a] -> [a]
rotatebyn 0 l = l
rotatebyn n l = rotatebyn (n-1) (rotateone n)
但這就像@dfeuer所說的那樣效率不高:旋轉列表需要O(n)時間,如果我們需要進行k次操作,則時間復雜度為O(nk) 。
此外請注意,以上函數僅在n
大於或等於零的情況下起作用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.