在 Haskell 中,我定義了一個遞回函式。在這個遞回函式中,我需要訪問串列的元素。
這個串列是一個不同整數的串列,它是由另一個函式創建的。如果我where lst = func_that_creates_list在此遞回函式中指定,則每次都會創建串列,這非常耗時。
因此,不知何故,我只需要呼叫一次創建此串列的函式,然后在整個遞回函式中使用此串列,但我現在不知道如何執行此操作。誰能幫我?
makeList :: Int -> [Integer]
makeList n = map (^2) [0..n]
recFunc :: Int -> [Integer]
recFunc 0 = 1
recFunc n = recFunc (n-1) 2 * x!!n where n = makeList n
uj5u.com熱心網友回復:
這是 common go-function 習語的一個實體。
recFunc :: Int -> [Integer]
recFunc n = go n
where go 0 = 1
go n' = recFunc (n'-1) 2 * x!!n'
x = makeList n
但事實上,這實際上不會提高性能,因為您使用的是 evil!!運算子。直接索引到預先計算的串列基本上與從頭開始計算一樣糟糕。向量的不同故事,但真的沒有理由在這里使用直接訪問。
正確的方法是邊走邊解構串列:
recFunc n = go n . reverse $ makeList n
where go n' (xω:x) = recFunc (n'-1) x 2 * xω
go _ _ = 1
其實n'現在爭論是不必要的
recFunc = go . reverse . makeList
where go (xω:x) = recFunc x 2 * xω
go [] = 1
仍然不是最佳選擇,因為您正在攜帶大量懶惰的 thunk,使用嚴格的累加器更好,這也使得reverse不必要的。(好吧,無論如何在這個例子中它是不必要的......)
recFunc = go 1 . makeList
where go acc (x?:x) = acc `seq` go (2*x? acc) x
go acc [] = acc
但是這個函式模式已經實作為foldl':
import Data.List
recFunc = foldl' (\acc x -> 2*x acc) 1 . makeList
或者干脆
recFunc = (1 ) . sum . map (2*) . makeList
然后你當然也可以行內makeList并融合地圖:
recFunc n = 1 sum ((2*) . (^2) <$> [0..n])
uj5u.com熱心網友回復:
你在定義中有一個錯字。更正了,是
makeList :: Int -> [Integer]
makeList n = map (^2) [0..n]
recFunc :: Int -> [Integer]
recFunc 0 = 1
recFunc n = recFunc (n-1) 2 * xs!!n
where xs = makeList n
我們可以擴展一些更基本的情況,注意重命名變數,以便所有名稱都是唯一的:
recFunc 0 = 1
recFunc 1 = recFunc 0 2 * xs1!!1
where xs1 = makeList 1
= 1 2 * xs1!!1
where xs1 = makeList 1
recFunc 2 = recFunc 1 2 * xs2!!2
where xs2 = makeList 2
= 1 2 * xs1!!1 2 * xs2!!2
where xs1 = makeList 1
xs2 = makeList 2
recFunc 3 = recFunc 2 2 * xs3!!3
where xs3 = makeList 3
= 1 2 * xs1!!1 2 * xs2!!2 2 * xs3!!3
where xs1 = makeList 1
xs2 = makeList 2
xs3 = makeList 3
-- ....
我們確實看到了您指出的問題。但是仔細想想,所有生成的串列都是同一個串列的前綴:
xs1 !! 1 == xs2 !! 1 == xs3 !! 1 == ....
所以我們可以將上面的內容重寫為
recFunc 0 = 1
recFunc 1 = 1 2 * xs3!!1
where xs3 = makeList 3
recFunc 2 = 1 2 * xs3!!1 2 * xs3!!2
where xs3 = makeList 3
recFunc 3 = 1 2 * xs3!!1 2 * xs3!!2 2 * xs3!!3
where xs3 = makeList 3
-- ....
突然問題就消失了:
recFunc n = 1 sum [ 2*xs!!i | i <- [1..n] ]
where xs = makeList n
重復掃描串列到順序索引是二次計算,但最終結果是我們只是按順序訪問串列的元素,從串列中的第二個元素開始:
recFunc n = 1 sum [ 2*x | x <- tail xs]
where xs = makeList n
= 1 sum [ 2*x | x <- tail $ map (^2) [0..n]]
= 1 sum [ 2*x | x <- map (^2) [1..n]]
= 1 sum [ 2*x^2 | x <- [1..n]]
或者,迭代地,
recFuncList = scanl ( ) 1 . map (2*) .
scanl ( ) 1 . iterate ( 2) $ 3
= scanl (\acc x -> acc 2*x) 1 .
scanl (\acc x -> acc x) 1 . iterate ( 2) $ 3
-- 3 5 7 9 11 13 15 17 19 21 ...
-- 1 4 9 16 25 36 49 64 81 100 121 ...
-- 1 3 11 29 61 111 183 281 409 571 771 1013 ...
= scanl ( ) 1 . scanl ( ) 2 . iterate ( 4) $ 6
無論你喜歡哪個。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/330319.html
上一篇:從動態嵌套陣列生成物件的平面陣列
下一篇:Python_5
