|
Language::Functional - a module which makes Perl slightly more functional |
Language::Functional - a module which makes Perl slightly more functional
use Language::Functional ':all';
print 'The first ten primes are: ',
show(take(10, filter { prime(shift) } 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.
$x = inc 2; # 3
In Haskell:
inc :: a -> a inc k = 1 + k
$x = double 3; # 6
In Haskell:
double :: a -> a double k = k * 2
$x = square 3; # 9
In Haskell:
square :: a -> a square k = k * k
$x = gcd(144, 1024); # 16
In Haskell:
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)
$x = lcm(144, 1024); # 9216
In Haskell:
lcm :: (Integral a) => a -> a -> a lcm _ 0 = 0 lcm 0 _ = 0 lcm x y = abs ((x `quot` gcd x y) * y)
$x = id([1..6]); # [1, 2, 3, 4, 5, 6].
In Haskell:
id :: a -> a id x = x
$x = const(4, 5); # 4
In Haskell:
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:
$x = Until { shift() % 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)
$x = fst([1, 2]); # 1
In Haskell:
fst :: (a,b) -> a fst (x,_) = x
$x = snd([1, 2]); # 2
In Haskell:
snd :: (a,b) -> a snd (_,y) = y
$x = head([1..6]); # 1
In Haskell:
head :: [a] -> a head (x:_) = x
$x = Last([1..6]); # 6
In Haskell:
last :: [a] -> a last [x] = x last (_:xs) = last xs
$x = tail([1..6]); # [2, 3, 4, 5, 6]
In Haskell:
tail :: [a] -> [a] tail (_:xs) = xs
$x = init([1..6]); # [1, 2, 3, 4, 5]
In Haskell:
init :: [a] -> [a] init [x] = [] init (x:xs) = x : init xs
$x = null([1, 2]); # False
In Haskell:
null :: [a] -> Bool null [] = True null (_:_) = False
$x = Map { double(shift) } [1..6]; # [2, 4, 6, 8, 10, 12]
In Haskell:
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:
$x = filter(\&even, [1..6]); # [2, 4, 6]
In Haskell:
filter :: (a -> Bool) -> [a] -> [a] filter p xs = [ x | x <- xs, p x ]
concat([[1..3], [4..6]]); # [1, 2, 3, 4, 5, 6]
In Haskell:
concat :: [[a]] -> [a] concat = foldr (++) []
TODO: Make sure this works with infinite lists!
$x = Length([1..6]); # 6
In Haskell:
length :: [a] -> Int length = foldl' (\n _ -> n + 1) 0
TODO Make sure this works!
$x = foldl { shift() + shift() } 0, [1..6]; # 21
In Haskell:
foldl :: (a -> b -> a) -> a -> [b] -> a foldl f z [] = z foldl f z (x:xs) = foldl f (f z x) xs
$x = foldl1 { shift() + shift() } [1..6]; # 21
In Haskell:
foldl1 :: (a -> a -> a) -> [a] -> a foldl1 f (x:xs) = foldl f x xs
$x = scanl { shift() + shift() }, 0, [1..6]; # [0, 1, 3, 6, 10, 15, 21]
In Haskell:
scanl :: (a -> b -> a) -> a -> [b] -> [a]
scanl f q xs = q : (case xs of
[] -> []
x:xs -> scanl f (f q x) xs)
$x = scanl1 { shift() + shift() } [1..6]; # [1, 3, 6, 10, 15, 21]
In Haskell:
scanl1 :: (a -> a -> a) -> [a] -> [a] scanl1 f (x:xs) = scanl f x xs
$x = foldr { shift() + shift() } 0, [1..6] ; # 21
In Haskell:
foldr :: (a -> b -> b) -> b -> [a] -> b foldr f z [] = z foldr f z (x:xs) = f x (foldr f z xs)
$x = foldr1 { shift() + shift() } [1..6]; # 21
In Haskell:
foldr1 :: (a -> a -> a) -> [a] -> a foldr1 f [x] = x foldr1 f (x:xs) = f x (foldr1 f xs)
$x = scanr { shift() + shift() } 0, [1..6];
# [0, 6, 11, 15, 18, 20, 21]
In Haskell:
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
$x = scanr1 { shift() + shift() } [1..6];
# [6, 11, 15, 18, 20, 21]
In Haskell:
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:
$x = take(8, iterate { shift() * 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)
$x = take(4, repeat(42)); # [42, 42, 42, 42].
In Haskell:
repeat :: a -> [a] repeat x = xs where xs = x:xs
$x = replicate(5, 1); # [1, 1, 1, 1, 1]
In Haskell:
replicate :: Int -> a -> [a] replicate n x = take n (repeat x)
$x = take(2, [1..6]); # [1, 2]
In Haskell:
take :: Int -> [a] -> [a] take 0 _ = [] take _ [] = [] take n (x:xs) | n>0 = x : take (n-1) xs take _ _ = error "Prelude.take: negative argument"
$x = drop(2, [1..6]); # [3, 4, 5, 6]
In Haskell:
drop :: Int -> [a] -> [a] drop 0 xs = xs drop _ [] = [] drop n (_:xs) | n>0 = drop (n-1) xs drop _ _ = error "Prelude.drop: negative argument"
$x = splitAt(2, [1..6]);# [[1, 2], [3, 4, 5, 6]]
In Haskell:
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"
$x = takeWhile { shift() <= 4 } [1..6]; # [1, 2, 3, 4]
In Haskell:
takeWhile :: (a -> Bool) -> [a] -> [a]
takeWhile p [] = []
takeWhile p (x:xs)
| p x = x : takeWhile p xs
| otherwise = []
$x = dropWhile { shift() <= 4 } [1..6]; # [5, 6]
In Haskell:
dropWhile :: (a -> Bool) -> [a] -> [a]
dropWhile p [] = []
dropWhile p xs@(x:xs')
| p x = dropWhile p xs'
| otherwise = xs
$x = span { shift() <= 4 }, [1..6];
# [[1, 2, 3, 4], [5, 6]]
In Haskell:
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'
$x = break { shift() >= 4 }, [1..6]; # [[1, 2, 3], [4, 5, 6]]
In Haskell:
break :: (a -> Bool) -> [a] -> ([a],[a]) break p = span (not . p)
$x = 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''
$x = words("hey how random"); # ['hey', 'how', 'random']
In Haskell:
words :: String -> [String]
words s = case dropWhile isSpace s of
"" -> []
s' -> w : words s''
where (w,s'') = break isSpace s'
$x = unlines(['A', 'B', 'C']); # "A\nB\nC";
In Haskell:
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...)
$x = unwords(["hey","how","random"]); # 'hey how random'
In Haskell:
unwords :: [String] -> String unwords [] = [] unwords ws = foldr1 (\w s -> w ++ ' ':s) ws
$x = Reverse([1..6]); # [6, 5, 4, 3, 2, 1]
In Haskell:
reverse :: [a] -> [a] reverse = foldl (flip (:)) []
$x = And([1, 1, 1]); # 1
In Haskell:
and :: [Bool] -> Bool and = foldr (&&) True
$x = Or([0, 0, 1]); # 1
In Haskell:
or :: [Bool] -> Bool or = foldr (||) False
$x = any { even(shift) } [1, 2, 3]; # 1
In Haskell:
any :: (a -> Bool) -> [a] -> Bool any p = or . map p
$x = all { odd(shift) } [1, 1, 3]; # 1
In Haskell:
all :: (a -> Bool) -> [a] -> Bool all p = and . map p
$x = elem(2, [1, 2, 3]); # 1
In Haskell:
elem :: Eq a => a -> [a] -> Bool elem = any . (==)
$x = notElem(2, [1, 1, 3]); # 1
In Haskell:
notElem :: Eq a => a -> [a] -> Bool notElem = all . (/=)
$x = lookup(3, [1..6]); # 4
In Haskell:
lookup :: Eq a => a -> [(a,b)] -> Maybe b
lookup k [] = Nothing
lookup k ((x,y):xys)
| k==x = Just y
| otherwise = lookup k xys
TODO: Make sure this works with infinite lists
$x = 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.
|
Language::Functional - a module which makes Perl slightly more functional |