我正在尝试在haskell中编写一个hyperoperation函数.
它通常是写的,ackermann(a,b,n)
但对于部分应用目的,我认为n
放在第一位更有意义.因此,我称之为hypOp n a b
我发现最自然的形式折叠ao replicate
列表如下:
Prelude> replicate 3 5
[5,5,5]
Prelude> foldr1 (*) $ replicate 3 5
125
Run Code Online (Sandbox Code Playgroud)
根据折叠的函数参数,这可以是加法,多重,取幂,四元组等.
非正式概述:
hypOp 0 a _ = succ a
hypOp 1 a b = a + b = foldr1 (succ a) (replicate b a) --OFF BY ONE ISSUES, TYPE ISSUES
hypOp 2 a b = a * b = foldr1 (+) $ replicate b a
hypOp 3 a b = a ^ b = foldr1 (*) $ replicate b a
hypOp 4 a b = = foldr1 (^)
Run Code Online (Sandbox Code Playgroud)
出于联想的原因,我的印象是我必须使用正确的折叠,这是不幸的,因为左折叠(foldl'
)的严格性是有用的.
右侧与左侧折叠问题
Prelude> foldl1 (^) $ replicate 4 2 --((2^2)^2)^2 = (4^2)^2 = 16^2 = 256 != 2 tetra 4
256
Prelude> foldr1 (^) $ replicate 4 2 --(2^(2^(2^2))) = 2^16 = 65536 == 2 tetra 4
65536
Run Code Online (Sandbox Code Playgroud)
当我开始使用后继函数时,我会得到一个一个一个问题.所以我使用(+)作为我的基础折叠的函数
Prelude> let add a b = foldr1 (\a b -> succ b) $ replicate b a
Prelude> add 5 4
8
Prelude> add 10 5 --always comes out short by one, so i cant build off this
14
Run Code Online (Sandbox Code Playgroud)
前几个n
值,"手动"完成:
Prelude> let mul a b = foldr1 (+) $ replicate b a
Prelude> let exp a b = foldr1 mul $ replicate b a
Prelude> let tetra a b = foldr1 exp $ replicate b a
Prelude> let pent a b = foldr1 tetra $ replicate b a
Prelude> let sixate a b = foldr1 pent $ replicate b a
Prelude> mul 2 3 --2+2+2
6
Prelude> exp 2 3 --2*2*2
8
Prelude> tetra 2 3 --2^(2^2)
16
Prelude> pent 2 3 --2 tetra (2 tetra 2)
65536
Prelude> sixate 2 3
*** Exception: stack overflow
Run Code Online (Sandbox Code Playgroud)
我通过上述方法尝试正式定义:
hypOp :: Int -> Int -> Int -> Int
hypOp 0 a b = succ a
hypOp 1 a b = (+) a b --necessary only bc off-by-one error described above
hypOp n a b = foldr1 (hypOp $ n-1) (replicate b a)
Run Code Online (Sandbox Code Playgroud)
其他尝试twith递归数组(没有任何显着的不同):
let arr = array (0,5) ( (0, (+)) : [(i, (\a b -> foldr1 (arr!(i-1)) (replicate b a)) ) | i <- [1..5]])
-- (arr!0) a b makes a + b
-- (arr!1) a b makes a * b, etc.
Run Code Online (Sandbox Code Playgroud)
所以我的问题是......
succ
seq
吗?我可以用某种方式foldl1'
代替foldr1
并避免上述问题?请参见第 3 点。虽然以这种方式定义这些操作是可行的,并且可以在没有溢出的情况下完成,但这是一种效率极低的方法。您的运行时间与答案是线性的,因为您最终会进行重复加法。
你之所以得到差一的原因基本上是因为你使用的是身份foldr1 f
而不是foldr f
身份。
foldr (+) 0 [a, a, a] = a + (a + (a + 0)))
foldr1 (+) [a, a, a] = a + (a + a)
Run Code Online (Sandbox Code Playgroud)
+
请注意,在 的情况下少了一个 的应用foldr1
。
简单地将参数的顺序更改为 怎么样(^)
?这样,您可以使用左折叠:
Prelude Data.List> foldl1 (flip (^)) $ replicate 4 2
65536
Run Code Online (Sandbox Code Playgroud)
现在您可以使用严格版本foldl1'
. 它不再溢出,但效率当然极低。