[Haskell-cafe] Strange subtract operator behavior - and lazy
naturals
John Meacham
john at repetae.net
Tue Oct 16 22:36:53 EDT 2007
On Wed, Oct 17, 2007 at 03:13:23AM +0100, Lennart Augustsson wrote:
> If naturals have a perfectly reasonable subtraction then they also have a
> perfectly reasonable negate; the default is 0-x.
>
> (Oh, subtraction wasn't THAT reasonable, you say. :) )
I suppose I was overextending the use of 'perfectly reasonable' here. :)
tangent:
if anyone is interested, Although I bet this has been implemented a
hundred times over, I have attached my lazy naturals module below just
for larks. It is quite efficient as such things go and very lazy. for
instance (genericLength xs > 5) will only evaluate up to the 5th element
of the list before returning a result. and ((1 `div` 0) > 17) is true,
not bottom.
Anyone have any comments on my lazy multiplication algorithm? since each
number is of the form (x + rx) (an integer, plus the lazy remainder) I
just did the multiplicitive expansion
(x + rx) * (y + ry) -> x*y + x*ry + y*rx + rx*ry
then I simplify to
(x + rx) * (y + ry) -> x*y + x*ry + rx*(y + ry)
which saves a nice recursive call to * speeding thinsg up signifigantly.
but is there a better way?
since (+) is lazy, we can still get a good lazy result without
evaluating the tails when multiplying... that is nice.
also, what do you think
n `mod` 0 should be? I can see arguments for it being 'n', 0, or
Infinity depending on how you look at it.. hmm..
If anyone wants me to clean this up and package it as a real module, I
would be happy to do so.
sorry for the tangent. just one of those days.
John
--
John Meacham - ⑆repetae.net⑆john⑈
-------------- next part --------------
-- 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
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
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