在Haskell数组创建中允许哪些递归调用?
作为使用Haskell数组的一个简短练习,我想实现一个给出前n个(奇数)素数的函数。下面的代码(使用GHC 7.10.3编译)在运行时产生循环错误。“Haskell的温和介绍”在数组创建中使用递归调用来计算斐波那契数(,13.2,下面的代码供参考),这很好。我的问题是: 这两种递归创建方法的区别在哪里?创建数组时通常允许哪些递归调用 我的代码:在Haskell数组创建中允许哪些递归调用?,haskell,recursion,Haskell,Recursion,作为使用Haskell数组的一个简短练习,我想实现一个给出前n个(奇数)素数的函数。下面的代码(使用GHC 7.10.3编译)在运行时产生循环错误。“Haskell的温和介绍”在数组创建中使用递归调用来计算斐波那契数(,13.2,下面的代码供参考),这很好。我的问题是: 这两种递归创建方法的区别在哪里?创建数组时通常允许哪些递归调用 我的代码: import Data.Array.Unboxed main = putStrLn $ show $ (primes 500)!500 --arbit
import Data.Array.Unboxed
main = putStrLn $ show $ (primes 500)!500 --arbitrary example
primes :: Int -> UArray Int Int
primes n = a
where
a = array (1,n) $ primelist 1 [3,5..]
primelist i (m:ms) =
if all (not . divides m) [ a!j | j <- [1..(i-1)]]
then (i ,m) : primelist (succ i) ms
else primelist i ms
divides m k = m `mod` k == 0
import Data.Array.unbox
main=putStrLn$show$(素数500)!500——任意示例
素数::Int->UArray Int
素数n=a
哪里
a=数组(1,n)$primelist 1[3,5..]
素数表i(m:ms)=
如果all(非.除m)[a!j | j数组Int
fibs n=a,其中a=数组(0,n)([(0,1),(1,1)]++
[(i,a!(i-2)+a!(i-1))| i更新:我想我终于明白发生了什么。array
在列表元素上是懒惰的,但在脊椎上是不必要的严格
例如,这会导致
异常
test :: Array Int Int
test = array (1,2) ((1,1) : if test!1 == 1 then [(2,2)] else [(2,100)])
不像
test :: Array Int Int
test = array (1,2) ((1,1) : [(2, if test!1 == 1 then 2 else 100)])
因此,只要递归只影响值,它就可以工作
工作版本:
main :: IO ()
main = do
putStrLn $ show $ (primes 500)!500 --arbitrary example
-- A spine-lazy version of array
-- Assumes the list carries indices lo..hi
arraySpineLazy :: (Int, Int) -> [(Int, a)] -> Array Int a
arraySpineLazy (lo,hi) xs = array (lo,hi) $ go lo xs
where
go i _ | i > hi = []
go i ~((_,e):ys) = (i, e) : go (succ i) ys
primes :: Int -> Array Int Int
primes n = a
where
a :: Array Int Int
a = arraySpineLazy (1,n) $ primelist 1 (2: [3,5..])
primelist :: Int -> [Int] -> [(Int, Int)]
primelist i _ | i > n = []
primelist _ [] = [] -- remove warnings
primelist i (m:ms) =
if all (not . divides m) [ a!j | j <- [1..(i-1)]]
then (i ,m) : primelist (succ i) ms
else primelist i ms
divides m k = m `mod` k == 0
这递归地定义了x=0;y=0
。但是,要使递归工作,必须使该对是惰性的。否则,它将生成无限递归,如下所示:
let p = case p of (x,y) -> (0,x)
上面,p
在暴露(,)
对构造函数之前对自身进行求值,因此出现了一个无限循环
let p = (0, case p of (x,y) -> x)
将起作用,因为p
在调用自身之前生成(,)
。但是请注意,这依赖于构造函数(,)
在返回之前不评估组件--它必须是惰性的,并且立即返回,而组件将在以后进行评估
在操作上,一对被构造为具有内部tho thunks:两个指向代码的指针,稍后将对结果进行计算。因此,这对指针实际上不是一对整数,而是一对指向整数的间接指针。这称为“装箱”,是实现惰性所必需的,即使它会带来一点计算成本
根据定义,未装箱的数据结构,如未装箱的数组,避免装箱,因此它们是严格的,而不是懒惰的,并且它们不能支持相同的递归方法。由于斐波那契示例有效,这意味着数组具有正确的懒散属性。如果在代码示例中用数组替换UArray,则问题应该得到解决(和import Data.Array)。不幸的是,修改后的代码在运行时仍会产生循环错误。@sf1请参阅我上次的编辑。这比我想象的要微妙得多。
let p = case p of (x,y) -> (0,x)
let p = (0, case p of (x,y) -> x)