[Haskell-cafe] Re: Shouldn't this loop indefinitely => take (last [0..]) [0..]

John Meacham john at repetae.net
Sun Apr 6 10:12:24 EDT 2008


On Fri, Apr 04, 2008 at 04:46:22PM +0100, Neil Mitchell wrote:
> Where length xs = 1 and ys = 1000. This takes 1000 steps to tell the
> Int's aren't equal, since we don't have proper lazy naturals. If we
> did, it would take 2 steps.
> 
> Read this: http://citeseer.ist.psu.edu/45669.html - it argues the
> point I am trying to make, but much better.

I implemented this efficient lazy natural class once upon a time. it
even has things like lazy multiplication:



-- Copyright (c) 2007 John Meacham (john at repetae dot net)
-- 
-- Permission is hereby granted, free of charge, to any person obtaining a
-- copy of this software and associated documentation files (the
-- "Software"), to deal in the Software without restriction, including
-- without limitation the rights to use, copy, modify, merge, publish,
-- distribute, sublicense, and/or sell copies of the Software, and to
-- permit persons to whom the Software is furnished to do so, subject to
-- the following conditions:
-- 
-- The above copyright notice and this permission notice shall be included
-- in all copies or substantial portions of the Software.
-- 
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-- OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

-- efficient lazy naturals

module Util.LazyNum where

-- Nat data type is eqivalant to a type restricted lazy list that is strict in
-- its elements.
--
-- Invarients: (Sum x _) => x > 0
-- in particular (Sum 0 _) is _not_ valid and must not occur.

data Nat = Sum !Integer Nat | Zero
    deriving(Show)

instance Eq Nat where
    Zero == Zero = True
    Zero == _ = False
    _ == Zero = False
    Sum x nx == Sum y ny = case compare x y of
        EQ -> nx == ny
        LT -> nx == Sum (y - x) ny
        GT -> Sum (x - y) nx == ny


instance Ord Nat where
    Zero <= _ = True
    _ <= Zero = False
    Sum x nx <= Sum y ny = case compare x y of
        EQ -> nx <= ny
        LT -> nx <= Sum (y - x) ny
        GT -> Sum (x - y) nx <= ny

    Zero `compare` Zero = EQ
    Zero `compare` _ = LT
    _    `compare` Zero = GT
    Sum x nx `compare` Sum y ny = case compare x y of
        EQ -> nx `compare` ny
        LT -> nx `compare` Sum (y - x) ny
        GT -> Sum (x - y) nx `compare` ny

    x < y = not (x >= y)
    x >= y = y <= x
    x > y = y < x


instance Num Nat where
    Zero + y = y
    Sum x n1 + y = Sum x (y + n1)
    --x + Zero = x
    --Sum x n1 + Sum y n2 = Sum (x + y) (n1 + n2)

    Zero - _ = zero
    x - Zero = x
    Sum x n1 - Sum y n2 = case compare x y of
        GT -> Sum (x - y) n1 - n2
        EQ -> n1 - n2
        LT -> n1 - Sum (y - x) n2
    negate _ = zero
    abs x = x
    signum Zero = zero
    signum _ = one
    fromInteger x = if x <= 0 then zero else Sum x Zero

    Zero * _ = Zero
    _ * Zero = Zero
    (Sum x nx) * (Sum y ny) = Sum (x*y) ((f x ny) + (nx * (fint y + ny))) where
        f y Zero = Zero
        f y (Sum x n) = Sum (x*y) (f y n)



instance Real Nat where
    toRational n = toRational (toInteger n)

instance Enum Nat where
    succ x = Sum 1 x
    pred Zero = Zero
    pred (Sum n x) = if n == 1 then x else Sum (n - 1) x
    enumFrom x = x:[ Sum n x | n <- [1 ..]]
    enumFromThen x y = x:y:f (y + z) where
        z = y - x
        f x = x:f (x + z)
    toEnum = fromIntegral
    fromEnum = fromIntegral

-- d > 0
doDiv :: Nat -> Integer -> Nat
doDiv n d = f 0 n where
    f _ Zero = 0
    f cm (Sum x nx) = sum d (f m nx) where
        (d,m) = (x + cm) `quotRem` d
        sum 0 x = x
        sum n x = Sum n x

doMod :: Nat -> Integer -> Nat
doMod n d = f 0 n where
    f 0 Zero = Zero
    f r Zero = fint r
    f r (Sum x nx) = f ((r + x) `rem` d) nx

instance Integral Nat where
    _ `div` Zero = infinity
    n1 `div` n2 | n1 < n2 = 0
                 | otherwise = doDiv n1 (toInteger n2)
    n1 `mod` Zero = n1 -- XXX
    n1 `mod` n2 | n1 < n2 = n1
                | otherwise = doMod n1 (toInteger n2)
    n `divMod` Zero = (infinity,n)
    n `divMod` d | n < d = (0,n)
                 | otherwise = let d' = toInteger d in (doDiv n d',doMod n d')
    quotRem = divMod
    quot = div
    rem = mod
    toInteger n = f 0 n where
        f n _ | n `seq` False = undefined
        f n Zero = n
        f n (Sum x n1) = let nx = n + x in nx `seq` f nx n1

-- convert to integer unless it is too big, in which case Nothing is returned

natToInteger :: Integer -> Nat -> Maybe Integer
natToInteger limit n = f 0 n where
    f n _ | n > limit = Nothing
    f n Zero = Just n
    f n (Sum x n1) = let nx = n + x in nx `seq` f nx n1

natShow :: Nat -> String
natShow n = case natToInteger bigNum n of
    Nothing -> "(too big)"
    Just v -> show v

natFoldr :: (Integer -> b -> b) -> b -> Nat -> b
natFoldr cons nil n = f n where
    f Zero = nil
    f (Sum x r) = cons x (f r)

-- some utility routines

natEven :: Nat -> Bool
natEven n = f True n where
    f r Zero = r
    f r (Sum x n) = if even x then f r n else f (not r) n

natOdd :: Nat -> Bool
natOdd n = not (natEven n)

{-# RULES "even/natEven" even = natEven #-}
{-# RULES "odd/natOdd"   odd  = natOdd #-}

zero = Zero
one = Sum 1 Zero
infinity = Sum bigNum infinity
bigNum = 100000000000
fint x = Sum x Zero

-- random testing stuff for ghci

ti op x y = (toInteger $ x `op` y, toInteger x `op` toInteger y)

depth n | n <= 0 = error "depth exceeded"
        | otherwise = Sum n (depth $ n - 1)

depth' n | n <= 0 = Zero
         | otherwise = Sum n (depth' $ n - 1)





More information about the Haskell-Cafe mailing list