我需要有关如何Integer
使用以下类型签名在 Haskell 中反转 a 的帮助:
reverseInt :: Integer -> Integer
reverseInt a = undefined -- help here
我需要Integer
像下面的示例一样反转输入数字。
例子:
> reverseInt 1989
9891
reverseInt :: Integer -> Integer
reverseInt = read . reverse . show
这不解决负数。如果你需要反转负数,你可以简单地使用
reverseInt :: Integer -> Integer
reverseInt x = (*) (signum x) . read . reverse . show . abs $ x
一种避免使用的方法show
是这个
reverseInt :: Integer -> Integer
reverseInt n = aux (n,0)
where aux (0,y) = y
aux (x,y) = let (x',y') = x `quotRem` 10
in aux (x',10*y+y')
它也适用于负数。
正如评论中所指出的,可以对 aux 函数进行一些优化,省略元组,这可以防止编译器评估 whnf(弱头范式),即如果编译器看到一个表达式,它会尽可能少地评估它,直到它看到了第一个构造函数。在 aux 的情况下,它是元组构造函数(,)
。使用 ghci + 可以实现对 whnf 的良好感觉:sprint
,请参阅 simon marlow 的书 ( http://chimera.labs.oreilly.com/books/1230000000929/ch02.html )。
reverseInt :: Int -> Int
reverseInt n = aux n 0
where aux 0 y = y
aux x y = let (x',y') = x `quotRem` 10
in aux x' (10*y+y')
正如@FrerichRaabe 所指出的,这完成了所需的任务,但理解它比 - 更棘手read . reverse . show
,我承认这一点。所以我将在一个例子中解释什么aux
是:
aux 1234 0 => 1234 `quotRem` 10 => (123,4) => x'= 123, y'= 4
=> aux 123 (0*10+4) => aux 123 4
aux 123 4 => 123 `quotRem` 10 => (12,3) => x'= 12, y'= 3
=> aux 12 (4*10+3) => aux 12 43
aux 12 43 => 12 `quotRem` 10 => (1,2) => x'= 1, y'= 2
=> aux 1 (43*10+2) => aux 1 432
aux 1 432 => 1 `quotRem` 10 => (0,1) => x'= 0, y'= 1
=> aux 0 (432*10+1) => aux 0 4321
aux 0 4321 => 4321
我决定我的包含show
是一个坏主意的假设是否合理。
短版是的!
长版本
我将所有版本放在一个文件中,并添加了我的方法的严格版本,以查看累加器中的非严格部分是否重要(事实证明:不是真的)。重要的是Int
在我的机器上(即 Int64)和Integer
我的版本之间进行选择
其他版本涉及show
并read
花费了更长的时间,但所有版本都在 6000 ns 左右——分别是 30 倍。10 与我的版本相比。有关更多信息,您可能需要查看该criterion
库生成的报告(感谢 Bryan O'Sullivan !!),保存下面的文件并在您的 shell 中键入。
$ ghc -O --make benchmarks.hs
$ ./benchmarks -o Report.html
RevInt.hs
{-# LANGUAGE BangPatterns #-}
module RevInt where
epsilon_fast :: Int-> Int
epsilon_fast n = aux n 0
where aux :: Int -> Int -> Int
aux 0 !y = y
aux x !y = let (x',y') = x `quotRem` 10
in aux x' (10*y+y')
epsilon_Integer :: Integer -> Integer
epsilon_Integer n = aux (n,0)
where aux (0,y) = y
aux (x,y) = let (x',y') = x `quotRem` 10
in aux (x',10*y+y')
epsilon_rInt :: Int-> Int
epsilon_rInt n = aux (n,0)
where aux (0,y) = y
aux (x,y) = let (x',y') = x `quotRem` 10
in aux (x',10*y+y')
epsilon_Integer' :: Integer -> Integer
epsilon_Integer' n = aux (n,0)
where aux (0,y) = y
aux (x,y) = let (x',y') = x `quotRem` 10
!z = 10*y+y'
in aux (x',z)
epsilon_rInt' :: Int-> Int
epsilon_rInt' n = aux (n,0)
where aux (0,y) = y
aux (x,y) = let (x',y') = x `quotRem` 10
!z = 10*y+y'
in aux (x',z)
fRaabe_Integer :: Integer -> Integer
fRaabe_Integer x | x < 0 = 0 - (read . reverse . tail . show $ x)
| otherwise = read . reverse . show $ x
fRaabe_Int :: Int -> Int
fRaabe_Int x | x < 0 = 0 - (read . reverse . tail . show $ x)
| otherwise = read . reverse . show $ x
zeta_Int :: Int -> Int
zeta_Int x = (*) (signum x) . read . reverse . show . abs $ x
zeta_Integer :: Integer -> Integer
zeta_Integer x = (*) (signum x) . read . reverse . show . abs $ x
基准.hs
import Criterion.Main
import RevInt
main :: IO ()
main = defaultMain
[bgroup "epsilon_fast" [ bench "123456789" $ whnf epsilon_fast 123456789 ,
bench "987654321" $ whnf epsilon_fast 987654321 ]
,bgroup "epsilon_Integer" [ bench "123456789" $ whnf epsilon_Integer 123456789 ,
bench "987654321" $ whnf epsilon_Integer 987654321 ]
,bgroup "epsilon_rInt" [ bench "123456789" $ whnf epsilon_rInt 123456789 ,
bench "987654321" $ whnf epsilon_rInt 987654321 ]
,bgroup "epsilon_Integer'"[ bench "123456789" $ whnf epsilon_Integer' 123456789 ,
bench "987654321" $ whnf epsilon_Integer' 987654321 ]
,bgroup "epsilon_rInt'" [ bench "123456789" $ whnf epsilon_rInt' 123456789 ,
bench "987654321" $ whnf epsilon_rInt' 987654321 ]
,bgroup "fRaabe_Int" [ bench "123456789" $ whnf fRaabe_Int 123456789 ,
bench "987654321" $ whnf fRaabe_Int 987654321 ]
,bgroup "fRaabe_Integer" [ bench "123456789" $ whnf fRaabe_Integer 123456789 ,
bench "987654321" $ whnf fRaabe_Integer 987654321 ]
,bgroup "zeta_Int" [ bench "123456789" $ whnf zeta_Int 123456789 ,
bench "987654321" $ whnf zeta_Int 987654321 ]
,bgroup "zeta_Integer" [ bench "123456789" $ whnf zeta_Integer 123456789 ,
bench "987654321" $ whnf zeta_Integer 987654321 ]]
将数字转换为字符串,将其还原,然后将反转的字符串转换回数字。Integer
值可以是负数,因此请注意前导-
:
reverseInt :: Integer -> Integer
reverseInt x | x < 0 = 0 - (read . reverse . tail . show $ x)
| otherwise = read . reverse . show $ x