Documente Academic
Documente Profesional
Documente Cultură
False and True are value constructors. And such constructor names must be unique
. False and True are zero arg constructors
The names of value constructors have no inherent meaning in Haskell (?? true, bu
t this is true for every programming language ever?)
Values of new types in Haskell are used in precisely the same way as built in ty
pes, and can be passed into functions as arguments, returned as results, stored
in data structures, used in pattern to match against
type Pos = (Int, Int)
data Move = North | South | East | West deriving Show
functions using this type
move
move
move
move
move
e.g:
Prelude> data Color = Red | Green | Blue deriving (Eq, Ord)
Prelude> Red < Green
True
hence False < True since data Bool = False | True
In the case of data types with value constructors taking arguments, the types of
these instances must also be instances of any 'derived' classes
eg.
data Shape = Circle Float | Rect Float Float deriving Eq
requires that Float is an instance of Eq (which it is)
to derive Maybe a as an Eq instance, requires that a is an instance type of Eq.
This becomes a class constraint on this parameter (? ! nothing formal, just a wa
y of saying a has to be instance type of Eq)
Values built using lists and tuples are ordered lexicographically so we get
> Rect 1.0 4.0 < Rect 2.0 3.0
True
> Rect 1.0 4.0 < Rect 1.0 3.0
False
8.6 Tautology Checker
data Prop = Const Bool
| Var char
| Not Prop
| And Prop Prop
| Imply Prop Prop
"Explicit use of parentheses for Haskell is not required, as parentheses within
Haskell can be used to indicate grouping."
I think this means something like
a => (b and c) is a valid expression as is (a => b) or c a
We can just do Implies (Var a) (And (Var b) (Var c)) -- note the brackets are Ha
skell's. I don't see what the problem being addressed is.
We use a lookup table, a value of the Assoc type introduced earlier
type Assoc k v = [(k,v)]
find :: Eq k => k -> Assoc k v -> v
find k t = head [k | (k',v) <- t, k == k']
type Subst = Assoc Char Bool -- note type aliasing
so for example the substitution [('a', False),('b', True)] assigns False and Tru
e respectively to the variables a and b.
Then we can write an eval function by pattern matching following the 'data Prop
= .. " declaration
eval
eval
eval
eval
eval
eval
:: Subst ->
_ (Const b)
s (Var c) =
s (Not x) =
s (And x y)
s (Implies
where
= "+"
= "-"
= "*"
= "/"
:: Op
Add _
Sub _
Mul _
Div _
:: Op
Add x
Sub x
Mul x
Div x
-> Int -> Int -> Int -- assumption valid check done
y = x + y
y = x - y
y = x * y
y = x / y
we have
>solution e [1,3,7,10,25,50] 765
True
So we have a way to determine if a given expression is a solution
This can be made more efficient by using a function isChoice that determines dir
ectly if one list is chosen from another (vs using choices that returns all the
possible choices from a list and then checking for elementhoo)
However right now efficiency is not a concern, and choices is used to define man
y other functions
9.6 Brute Force Solutions
Our first approach: Choose all possible expressions over a list of numbers. Then
test each one with 'solution'.
We start by defining a function split that returns all possible ways ofs splitti
ng a list into two non-empty lists that append to give the original list
split
split
split
split
and then
a function solutions that first generates all expressions from a given list of n
umbers, then select those expressions that successfully evaluate to give the tar
get
solutions :: [Int] -> Int -> [Expr] -- the first param is the list of numbers, 2
nd target
solutions ns n = [ e | ns' <- choices ns, e <- exprs ns', eval e == [n]]
9.7 Performance Testing
For running in the GHC compiler
main :: IO()
main e == print (solutions [1,3,5,7,10,15,25,50] 765)
On GHC v 7.10 on 2.8 GHz Intel Core Duo with 4 GB Ram,
1st solution returned in 0.108 seconds
all 780 solutions in 12.224 seconds
when target is 831, the empty list of solutions is returned in 12.802 seconds
So we can already beat TV show time limit.
the same structure as the 'exprs' fun but replaced by results with combine' inst
ead of combine
then instead of
solutions :: [Int] -> Int -> [Expr] -- the first param is the list of numbers, 2
nd target
solutions ns n = [e | ns' <- choices ns, e <- exprs ns', eval e == [n]]
we do
solutions' :: [Int] -> Int -> [Expr] -- note signature is the same
solutions' ns n = [e | ns' <- choices ns, (e,v) <- results ns', v == n ]
Measuring we get
as compared to the old
On GHC v 7.10 on 2.8 GHz Intel Core Duo with 4 GB Ram,
1st solution returned in 0.108 seconds
all 780 solutions in 12.224 seconds
now we have
1st solution generated in 0.014 seconds
all 780 solutions in 1.312 seconds
and similarly for no solutions 1.134 seconds (11 times faster)
9.9 Exploiting algebraic properties
Further optimization from this observation
solutions' generates only those expressions whose evaluations are successful and
generate valid results, but even among these many expressions are identical, w
ith rearrangements with commutative operators.
E.g: 2 + 3 and 3 + 2 are both generated.
Based on this observation we decide to take advantage of the following propertie
s
x + y = y + x
x * y = y * x
x * 1 = x
1 * x = x
x / 1 = x
We augment the valid function to get
valid
valid
valid
valid
valid
:: Op
Add x
Sub x
Mul x
Div x
v1
v2
vn
return the resulting value as t
e.g: an action that reads 3 values, discards the second, and returns the first a
nd third as a pair can be written as
act :: IO (Char, Char)
act = do x <- getChar
y <- getChar
z <- getChar
return (x,z)
3 further points
- the layout rule applies, so each action in the sequence must b
egin in precisely the same column
- as with list comprehensions the expressions v_i <- a_i are cal
led generators, because they generate values for the variables v_i.
- if the result value produced by a generatro v_i <- a_i is not
required, the generator can be abbreviated simply by a_i, whih has the same mean
ing as writing _ <- a_i
so the above function can be modified to
Start with a top level prompt that asks a player to enter a word then prompt the
second player to guess it
hangman :: IO ()
hangman = do putStrLn "Think of a word: "
word <- sgetLine
putStrLn "Try to guess it "
play word
To complete this we have to implement sgetLine and play
sgetLine reads a string of characters from the keyboard, like getLine but it ech
oes each character as a dash symbol - in order to keep the string secret.
sgetLine :: IO String
sgetLine = do x <- getCh
if x == '\n' then
do putChar x
return []
else
do putChar '_'
xs = s <- getLine
return (x:xs)
getCh :: IO Char
getCh = do hSetEcho StdIn False
x <- getChar
hSetEcho stdin True
return x
note:
Prelude> :t System.IO.hSetEcho
System.IO.hSetEcho :: GHC.IO.Handle.Types.Handle -> Bool -> IO ()
nction play implements the main loop by repeatedly prompting thesecond player to
enter a guess until it equals the secret word
play :: String -> IO()
play word - do putStr "?"
guess <- getLine
if guess == word then
putStrLn "You got it"
else
do putStrLn (match word guess)
play word
match returns which letters in the secret word occurs anywhere in the guess
match :: String -> String -> String
match xs ys = [if x elem ys then x else _ | x <- xs]
10.7 Nim
Fairly straight forward.
Final para makes the following points
Because Haskell is a pure language, we needed to supply the game state, which in
this case comprises the current board and player number, as explicit arguments