|
Functional - a module which makes Perl slightly more functional |
Functional - a module which makes Perl slightly more functional (think Haskell)
use Functional;
print 'The first ten primes are: ',
show(take(10, filter("prime", integers))), "\n";
Perl already contains some functional-like functions, such as
map and grep. The purpose of this module is to add other
functional-like functions to Perl, such as foldl and foldr, as
well as the use of infinite lists.
Think as to how you would express the first ten prime numbers in a simple way in your favourite programming language? So the example in the synopsis is a killer app, if you will (until I think up a better one ;-).
The idea is mostly based on Haskell, from which most of the functions are taken. There are a couple of major omissions: currying and types. Lists (and tuples) are simply Perl list references, none of this 'cons' business, and strings are simple strings, not lists of characters.
The idea is to make Perl slightly more functional, rather than completely replace it. Hence, this slots in very well with whatever else your program may be doing, and is very Perl-ish. Other modules are expected to try a much more functional approach.
The following functions are available. (Note: these should not be called as methods).
In each description, I shall give the Haskell definition (if I think it would help) as well as a useful example.
inc(2) = 3.
In Haskell:
inc :: a -> a
inc k = 1 + k
double(3) = 6.
In Haskell:
double :: a -> a
double k = k * 2
square(3) = 9.
In Haskell:
square :: a -> a
square k = 1 + k
gcd :: Integral a => a -> a -> a
gcd 0 0 = error "gcd 0 0 is undefined"
gcd x y = gcd' (abs x) (abs y)
where gcd' x 0 = x
gcd' x y = gcd' y (x `rem` y)
lcm :: (Integral a) => a -> a -> a
lcm _ 0 = 0
lcm 0 _ = 0
lcm x y = abs ((x `quot` gcd x y) * y)
id([1..6]) = [1, 2, 3, 4, 5, 6]. In Haskell:
id :: a -> a
id x = x
const :: a -> b -> a
const k _ = k
flip :: (a -> b -> c) -> b -> a -> c
flip f x y = f y x
p(x) is true, and
then return x at that point. eg: Until(sub { $_[0] % 10 == 0 }, ``inc'', 1) =
10. In Haskell:
until :: (a -> Bool) -> (a -> a) -> a -> a
until p f x = if p x then x else until p f (f x)
fst :: (a,b) -> a
fst (x,_) = x
snd :: (a,b) -> a
snd (_,y) = y
head([1..6]) = 1. In Haskell:
head :: [a] -> a
head (x:_) = x
Last([1..6]) = 6. In Haskell:
last :: [a] -> a
last [x] = x
last (_:xs) = last xs
tail([1..6]) = [2, 3, 4, 5, 6]. In Haskell:
tail :: [a] -> [a]
tail (_:xs) = xs
init([1..6]) = [1, 2, 3, 4, 5]. In Haskell:
init :: [a] -> [a]
init [x] = []
init (x:xs) = x : init xs
null :: [a] -> Bool
null [] = True
null (_:_) = False
map :: (a -> b) -> [a] -> [b]
map f xs = [ f x | x <- xs ]
p(xs) returns true. It is similar to the Perl command
'grep', but it also copes with infinite lists.
eg: filter(``even'', [1..6]) = [2, 4, 6]. In Haskell:
filter :: (a -> Bool) -> [a] -> [a]
filter p xs = [ x | x <- xs, p x ]
concat :: [[a]] -> [a]
concat = foldr (++) []
TODO: Make sure this works with infinite lists!
Length([1..6]) = 6. In Haskell:
length :: [a] -> Int length = foldl' (\n _ -> n + 1) 0
TODO Make sure this works!
foldl :: (a -> b -> a) -> a -> [b] -> a
foldl f z [] = z
foldl f z (x:xs) = foldl f (f z x) xs
foldl1 :: (a -> a -> a) -> [a] -> a
foldl1 f (x:xs) = foldl f x xs
scanl :: (a -> b -> a) -> a -> [b] -> [a]
scanl f q xs = q : (case xs of
[] -> []
x:xs -> scanl f (f q x) xs)
scanl1 :: (a -> a -> a) -> [a] -> [a]
scanl1 f (x:xs) = scanl f x xs
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f z [] = z
foldr f z (x:xs) = f x (foldr f z xs)
foldr1 :: (a -> a -> a) -> [a] -> a
foldr1 f [x] = x
foldr1 f (x:xs) = f x (foldr1 f xs)
scanr :: (a -> b -> b) -> b -> [a] -> [b]
scanr f q0 [] = [q0]
scanr f q0 (x:xs) = f x q : qs
where qs@(q:_) = scanr f q0 xs
scanr1 :: (a -> a -> a) -> [a] -> [a]
scanr1 f [x] = [x]
scanr1 f (x:xs) = f x q : qs
where qs@(q:_) = scanr1 f xs
f(f(f(x)))...) and so on. eg:
take(8, iterate(sub { $_[0]*2 }, 1)) = [1, 2, 4, 8, 16, 32, 64, 128].
In Haskell:
iterate :: (a -> a) -> a -> [a]
iterate f x = x : iterate f (f x)
repeat(42)) = [42, 42, 42, 42].
In Haskell:
repeat :: a -> [a]
repeat x = xs where xs = x:xs
replicate :: Int -> a -> [a]
replicate n x = take n (repeat x)
take :: Int -> [a] -> [a]
take 0 _ = []
take _ [] = []
take n (x:xs) | n>0 = x : take (n-1) xs
take _ _ = error "Prelude.take: negative argument"
drop :: Int -> [a] -> [a]
drop 0 xs = xs
drop _ [] = []
drop n (_:xs) | n>0 = drop (n-1) xs
drop _ _ = error "Prelude.drop: negative argument"
splitAt :: Int -> [a] -> ([a], [a])
splitAt 0 xs = ([],xs)
splitAt _ [] = ([],[])
splitAt n (x:xs) | n>0 = (x:xs',xs'') where (xs',xs'') = splitAt (n-1) xs
splitAt _ _ = error "Prelude.splitAt: negative argument"
takeWhile :: (a -> Bool) -> [a] -> [a]
takeWhile p [] = []
takeWhile p (x:xs)
| p x = x : takeWhile p xs
| otherwise = []
dropWhile :: (a -> Bool) -> [a] -> [a]
dropWhile p [] = []
dropWhile p xs@(x:xs')
| p x = dropWhile p xs'
| otherwise = xs
span :: (a -> Bool) -> [a] -> ([a],[a])
span p [] = ([],[])
span p xs@(x:xs')
| p x = (x:ys, zs)
| otherwise = ([],xs)
where (ys,zs) = span p xs'
break :: (a -> Bool) -> [a] -> ([a],[a])
break p = span (not . p)
lines(``A\nB\nC'') = ['A', 'B', 'C'].
In Haskell:
lines :: String -> [String]
lines "" = []
lines s = let (l,s') = break ('\n'==) s
in l : case s' of [] -> []
(_:s'') -> lines s''
words :: String -> [String]
words s = case dropWhile isSpace s of
"" -> []
s' -> w : words s''
where (w,s'') = break isSpace s'
unlines :: [String] -> String
unlines = concatMap (\l -> l ++ "\n")
(note that strings in Perl are not lists of characters, so this approach will not actually work...)
unwords([``hey'',``how'',``random'']) = 'hey how random'.
In Haskell:
unwords :: [String] -> String
unwords [] = []
unwords ws = foldr1 (\w s -> w ++ ' ':s) ws
Reverse([1..6]) = [6, 5, 4, 3, 2, 1]. In Haskell:
reverse :: [a] -> [a]
reverse = foldl (flip (:)) []
and :: [Bool] -> Bool
and = foldr (&&) True
or :: [Bool] -> Bool
or = foldr (||) False
any :: (a -> Bool) -> [a] -> Bool
any p = or . map p
all :: (a -> Bool) -> [a] -> Bool
all p = and . map p
elem :: Eq a => a -> [a] -> Bool
elem = any . (==)
notElem :: Eq a => a -> [a] -> Bool
notElem = all . (/=)
lookup :: Eq a => a -> [(a,b)] -> Maybe b
lookup k [] = Nothing
lookup k ((x,y):xys)
| k==x = Just y
| otherwise = lookup k xys
minimum([1..6]) = 1. In Haskell:
minimum :: Ord a => [a] -> a
minimum = foldl1 min
maximum([1..6]) = 6. In Haskell:
maximum :: Ord a => [a] -> a
maximum = foldl1 max
sum([1..6]) = 21. In Haskell:
sum :: Num a => [a] -> a
sum = foldl' (+) 0
product([1..6]) = 720. In Haskell:
product :: Num a => [a] -> a
product = foldl' (*) 1
zip :: [a] -> [b] -> [(a,b)]
zip = zipWith (\a b -> (a,b))
zipWith :: (a->b->c) -> [a]->[b]->[c]
zipWith z (a:as) (b:bs) = z a b : zipWith z as bs
zipWith _ _ _ = []
zip3 :: [a] -> [b] -> [c] -> [(a,b,c)]
zip3 = zipWith3 (\a b c -> (a,b,c))
zipWith3 :: (a->b->c->d) -> [a]->[b]->[c]->[d]
zipWith3 z (a:as) (b:bs) (c:cs)
= z a b c : zipWith3 z as bs cs
zipWith3 _ _ _ _ = []
unzip([1,7,2,8,3,9,4,10,5,11,6,12]) = ([1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12]).
unzip :: [(a,b)] -> ([a],[b])
unzip = foldr (\(a,b) ~(as,bs) -> (a:as, b:bs)) ([], [])
unzip3([1,3,5,2,4,6]) = ([1, 2], [3, 4], [5, 6]).
In Haskell:
unzip3 :: [(a,b,c)] -> ([a],[b],[c])
unzip3 = foldr (\(a,b,c) ~(as,bs,cs) -> (a:as,b:bs,c:cs))
([],[],[])
factors(100) = [1, 2, 4, 5, 10, 20, 25, 50, 100].
In Haskell:
factors x = [n | n <- [1..x], x `mod` n == 0]
primes = [n | n <- [2..], length (factors n) == 2]
Leon Brocard <acme@astray.com>
Copyright (C) 1999, Leon Brocard
This module is free software; you can redistribute it or modify it under the same terms as Perl itself.
|
Functional - a module which makes Perl slightly more functional |