Again I'm requesting comments about how a given design should be implemented in Haskell.
Thanks in advance to everyone providing helpful comments. Also I hope this could be an aid to other Haskell novices like me, having a practical sample code.
This time, we have a polymorphic function doSampling (in module Samples) that takes a generic function f and
a list of reals (indexes) and returns a Samples (indexes, values=f(indexes)). We want implement doSampling only once, as it doesn't matter if is f is a Polynomial or a Sinus. For that,
we have introduced an interface Function, and have Polynomial and Sinus types implement it.
The following is the design being implemented:
Edit 1:
There is a debate on the Function interface (class in Haskell). It has been suggested it is not actually necessary, as doSampling may take a "nude" function (Double -> Double) instead.
But, how to do it, if you need some extra state within the nude function (coeffs for a polynomial, amp+freq+phase for a sinus?
Edit 2:
Very good answers by kosmikus and by Chris Taylor. Thanks.
A key idea in both: have
doSampling :: (Double -> Double) -> [Double] -> Samples
This is: it takes a function (Double -> Double) (instead of Function) and list and returns samples.
My intention was to keep the state of Polynomials and Sinuses. That is not regarded in Chris answer, but it is in kosmikus'. On the other hand, the weak point in kosmikus version could be how to extend its Function definition if you don't have access to the source code.
I would also point out:
Chris' idea of encapsulating a polynomial or a sinus into a function (Double -> Double) by means of a factory function mkPolynomial or mkSinus that generates (using currying?) the desired function taking the apropriate parameters. (Although you can't consult the parameters later).
kosmikous' idea of using value to transform (also using currying?) a Function into a (Double -> Double)
Both answers are worth reading as they have other little Haskell tricks to reduce and simplify code.
In sum
Chris answers does not support keeping the state of a Polynomial or of a Sinus
kosmikus answers is not extensible: adding new type of functions (Cosinus ...)
my answer (being verbose) does overcome the previous downsides, and it would allow (this not necessary for the problem) impose Function types to have more associated-functions apart of value (in the sense of how an java-interfaces work).
My own approach
main (usage)
import Polynomial
import Sinus
import Function
import Samples
-- ...............................................................
p1 = Polynomial [1, 0, 0.5] -- p(x) = 1 + 0.5x^2
s1 = Sinus 2 0.5 3 -- f(x) = 2 sin(0.5x + 3)
-- ...............................................................
-- sample p1 from 0 to 5
m1 = doSampling p1 [0, 0.5 .. 5]
m2 = doSampling s1 [0, 0.5 .. 5]
-- ...............................................................
-- main
-- ...............................................................
main = do
putStrLn "Hello"
print $ value p1 2
print $ value s1 (pi/2)
print $ pairs m1
print $ pairs m2
Function
module Function where
-- ...............................................................
-- "class type" : the types belonging to this family of types
-- must implement the following functions:
-- + value : takes a function and a real and returns a real
-- ...............................................................
class Function f where
value :: f -> Double -> Double
-- f is a type variable, this is:
-- f is a type of the Function "family" not an actual function
Samples
module Samples where
import Function
-- ...............................................................
-- Samples: new data type
-- This is the constructor and says it requieres
-- two list, one for the indexes (xs values) and another
-- for the values ( ys = f (xs) )
-- this constructor should not be used, instead use
-- the "factory" function: new_Samples that performs some checks
-- ...............................................................
data Samples = Samples { indexes :: [Double] , values :: [Double] }
deriving (Show)
-- ...............................................................
-- constructor: it checks lists are equal size, and indexes are sorted
new_Samples :: [Double] -> [Double] -> Samples
new_Samples ind val
| (length ind) /= (length val) = samplesVoid
| not $ isSorted ind = samplesVoid
| otherwise = Samples ind val
-- ...............................................................
-- sample a funcion
-- it takes a funcion f and a list of indexes and returns
-- a Samples calculating the values array as f(indexes)
doSampling :: (Function f) => f -> [Double] -> Samples
doSampling f ind = new_Samples ind vals
where
vals = [ value f x | x <- ind ]
-- ...............................................................
-- used as "error" in the construction
samplesVoid = Samples [] []
-- ...............................................................
size :: Samples -> Int
size samples = length (indexes samples)
-- ...............................................................
-- utility function to get a pair (index,value) out of a Samples
pairs :: Samples -> [(Double, Double)]
pairs samples = pairs' (indexes samples) (values samples)
pairs' :: [Double] -> [Double] -> [(Double, Double)]
pairs' [] [] = []
pairs' [i] [v] = [(i,v)]
pairs' (i:is) (v:vs) = (i,v) : pairs' is vs
-- ...............................................................
-- to check whether a list is sorted (<)
isSorted :: (Ord t) => [t] -> Bool
isSorted [] = True
isSorted [e] = True
isSorted (e1:(e2:tail))
| e1 < e2 = isSorted (e2:tail)
| otherwise = False
Sinus
module Sinus where
-- ...............................................................
import Function
-- ...............................................................
-- Sinus: new data type
-- This is the constructor and says it requieres
-- a three reals
-- ...............................................................
data Sinus = Sinus { amplitude :: Double, frequency :: Double, phase :: Double }
deriving (Show)
-- ...............................................................
-- we say that a Sinus is a Function (member of the class Function)
-- and then, how value is implemented
instance Function Sinus where
value s x = (amplitude s) * sin ( (frequency s)*x + (phase s))
Polynomial
module Polynomial where
-- ...............................................................
import Function
-- ...............................................................
-- Polynomial: new data type
-- This is the constructor and says it requieres
-- a list of coefficients
-- ...............................................................
data Polynomial = Polynomial { coeffs :: [Double] }
deriving (Show)
-- ...............................................................
degree :: Polynomial -> Int
degree p = length (coeffs p) - 1
-- ...............................................................
-- we say that a Polynomial is a Function (member of the class Function)
-- and then, how value is implemented
instance Function Polynomial where
value p x = value' (coeffs p) x 1
-- list of coeffs -> x -> pw (power of x) -> Double
value' :: [Double] -> Double -> Double -> Double
value' (c:[]) _ pw = c * pw
value' (c:cs) x pw = (c * pw) + (value' cs x x*pw)
You certainly don't need the Function class. All this heavyweight class, instance, member variable fluff is one of the things that Haskell is designed to avoid. Pure functions can be much more flexible.
Here's a simple way of doing what you want.
type Sample = ([Double], [Double])
newSample xs vs
| isSorted xs && length xs == length vs = (indices, values)
| otherwise = ([], [])
pairs = uncurry zip
doSampling :: (Double -> Double) -> [Double] -> Sample
doSampling f xs = newSample xs (map f xs)
mkPolynomial :: [Double] -> (Double -> Double)
mkPolynomial coefs x = go coefs
where
go [] = 0
go (c:cs) = c + x * go cs
mkSinus :: Double -> Double -> Double -> (Double -> Double)
mkSinus amp freq phase x = amp * sin (freq * x + phase)
p1 = mkPolynomial [1, 0, 0.5] -- 1 + 0.5x^2
s1 = mkSinus 2 0.5 3 -- 2 sin(0.5x + 3)
m1 = doSampling p1 [0, 0.5 .. 5]
m2 = doSampling s1 [0, 0.5 .. 5]
main :: IO ()
main = do
print $ p1 2
print $ s1 (pi/2)
print $ pairs m1
print $ pairs m2
[Expanded my comment on request.]
I'd probably do this roughly as follows:
import Data.Functor
-- Use a datatype rather than a class. Yes, this makes it harder to
-- add new types of functions later, and in turn easier to define new
-- operations. ("expression problem")
data Function =
Sinus { amplitude :: Double, frequency :: Double, phase :: Double }
| Polynomial { coeffs :: [Double] }
deriving (Show)
-- Interpreting a Function as an actual function.
value :: Function -> (Double -> Double)
value (Sinus amp freq ph) x = amp * sin (freq * x + ph)
value (Polynomial cs) x = value' cs x
-- Rewrite value' to not require non-empty lists. This can also be
-- nicely written as a fold.
value' :: [Double] -> Double -> Double
value' [] _ = 0
value' (c:cs) x = c + x * value' cs x
data Samples = Samples { indexes :: [Double] , values :: [Double] }
deriving (Show)
-- Use Maybe to detect error conditions, instead of strange values
-- such as voidSamples.
newSamples :: [Double] -> [Double] -> Maybe Samples
newSamples ind val
| length ind /= length val = Nothing
| not $ isSorted ind = Nothing
| otherwise = Just (Samples ind val)
doSampling :: (Double -> Double) -> [Double] -> Maybe Samples
doSampling f ind = newSamples ind (map f ind)
isSorted :: (Ord t) => [t] -> Bool
isSorted [] = True
isSorted [e] = True
isSorted (e1:e2:es)
| e1 < e2 = isSorted (e2:es)
| otherwise = False
-- This is just zip.
pairs :: Samples -> [(Double, Double)]
pairs (Samples idxs vals) = zip idxs vals
p1 = Polynomial [1, 0, 0.5] -- p(x) = 1 + 0.5x^2
s1 = Sinus 2 0.5 3 -- f(x) = 2 sin(0.5x + 3)
m1 = doSampling (value p1) [0, 0.5 .. 5]
m2 = doSampling (value s1) [0, 0.5 .. 5]
-- The <$> maps over a Maybe.
main = do
putStrLn "Hello"
print $ value p1 2
print $ value s1 (pi/2)
print $ pairs <$> m1
print $ pairs <$> m2
Related
My code aims to create a word search puzzle. There is a data called Orientation representing the direction of each word in the puzzle.
data Orientation =
Forward | Back | Up | Down | UpForward | UpBack | DownForward | DownBack
deriving (Eq, Ord, Show, Read)
Now given a input of strings which is [String], I want to randomly assign each string an orientation like [(Orientation, String)]
assignWordDir :: [String] -> [(Orientation, String)]
assignWordDir [] = []
assignWordDir (s:strs) = (ori, s) : assignWordDir
where ori = pickOri [Forward, Back, Up, Down, UpForward, UpBack, DownForward, DownBack]
pickOri :: [a] -> IO a
pickOri xs = do
i <- randomRIO (0, len)
pure $ xs !! i
where len = length xs - 1
I cannot compile because the output of pickOri is IO Orientation, is there any suggestions on how to modify my code? Thanks a lot
Couldn't match expected type ‘[(IO Orientation, String)]’
with actual type ‘[String] -> [(Orientation, String)]’
You might consider modifying the functions so that they stay pure by taking a RandomGen parameter. The pickOri function, for example, might be modified thusly:
pickOri :: RandomGen g => g -> [a] -> (a, g)
pickOri rnd xs =
let len = length xs - 1
(i, g) = randomR (0, len) rnd
in (xs !! i, g)
It's necessary to return the new RandomGen value g together with the selected list element, so that it'll generate another pseudo-random number the next time around.
Likewise, you can modify assignWordDir like this:
assignWordDir :: RandomGen g => g -> [b] -> [(Orientation, b)]
assignWordDir _ [] = []
assignWordDir rnd (s:strs) = (ori, s) : assignWordDir g strs
where (ori, g) =
pickOri rnd [Forward, Back, Up, Down, UpForward, UpBack, DownForward, DownBack]
Notice that when recursing into to assignWordDir, the recursive function call uses the g it receives from pickOri.
You can use mkStdGen or newStdGen to produce RandomGen values. Here's an example using newStdGen:
*Q65132918> rnd <- newStdGen
*Q65132918> assignWordDir rnd ["foo", "bar", "baz"]
[(UpBack,"foo"),(Up,"bar"),(UpBack,"baz")]
*Q65132918> assignWordDir rnd ["foo", "bar", "baz"]
[(UpBack,"foo"),(Up,"bar"),(UpBack,"baz")]
Notice that when you use the same RandomGen value, you get the same sequence. That's because assignWordDir is a pure function, so that's expected.
You can, however, produce a new random sequence by creating or getting a new StdGen value:
*Q65132918> rnd <- newStdGen
*Q65132918> assignWordDir rnd ["foo", "bar", "baz"]
[(Up,"foo"),(Up,"bar"),(Forward,"baz")]
If you want to play with this in a compiled module, you can keep these functions as presented here, and then compose them with a newStdGen-generated StdGen in the main entry point.
I have random number generator
rand :: Int -> Int -> IO Int
rand low high = getStdRandom (randomR (low,high))
and a helper function to remove an element from a list
removeItem _ [] = []
removeItem x (y:ys) | x == y = removeItem x ys
| otherwise = y : removeItem x ys
I want to shuffle a given list by randomly picking an item from the list, removing it and adding it to the front of the list. I tried
shuffleList :: [a] -> IO [a]
shuffleList [] = []
shuffleList l = do
y <- rand 0 (length l)
return( y:(shuffleList (removeItem y l) ) )
But can't get it to work. I get
hw05.hs:25:33: error:
* Couldn't match expected type `[Int]' with actual type `IO [Int]'
* In the second argument of `(:)', namely
....
Any idea ?
Thanks!
Since shuffleList :: [a] -> IO [a], we have shuffleList (xs :: [a]) :: IO [a].
Obviously, we can't cons (:) :: a -> [a] -> [a] an a element onto an IO [a] value, but instead we want to cons it onto the list [a], the computation of which that IO [a] value describes:
do
y <- rand 0 (length l)
-- return ( y : (shuffleList (removeItem y l) ) )
shuffled <- shuffleList (removeItem y l)
return y : shuffled
In do notation, values to the right of <- have types M a, M b, etc., for some monad M (here, IO), and values to the left of <- have the corresponding types a, b, etc..
The x :: a in x <- mx gets bound to the pure value of type a produced / computed by the M-type computation which the value mx :: M a denotes, when that computation is actually performed, as a part of the combined computation represented by the whole do block, when that combined computation is performed as a whole.
And if e.g. the next line in that do block is y <- foo x, it means that a pure function foo :: a -> M b is applied to x and the result is calculated which is a value of type M b, denoting an M-type computation which then runs and produces / computes a pure value of type b to which the name y is then bound.
The essence of Monad is thus this slicing of the pure inside / between the (potentially) impure, it is these two timelines going on of the pure calculations and the potentially impure computations, with the pure world safely separated and isolated from the impurities of the real world. Or seen from the other side, the pure code being run by the real impure code interacting with the real world (in case M is IO). Which is what computer programs must do, after all.
Your removeItem is wrong. You should pick and remove items positionally, i.e. by index, not by value; and in any case not remove more than one item after having picked one item from the list.
The y in y <- rand 0 (length l) is indeed an index. Treat it as such. Rename it to i, too, as a simple mnemonic.
Generally, with Haskell it works better to maximize the amount of functional code at the expense of non-functional (IO or randomness-related) code.
In your situation, your “maximum” functional component is not removeItem but rather a version of shuffleList that takes the input list and (as mentioned by Will Ness) a deterministic integer position. List function splitAt :: Int -> [a] -> ([a], [a]) can come handy here. Like this:
funcShuffleList :: Int -> [a] -> [a]
funcShuffleList _ [] = []
funcShuffleList pos ls =
if (pos <=0) || (length(take (pos+1) ls) < (pos+1))
then ls -- pos is zero or out of bounds, so leave list unchanged
else let (left,right) = splitAt pos ls
in (head right) : (left ++ (tail right))
Testing:
λ>
λ> funcShuffleList 4 [0,1,2,3,4,5,6,7,8,9]
[4,0,1,2,3,5,6,7,8,9]
λ>
λ> funcShuffleList 5 "#ABCDEFGH"
"E#ABCDFGH"
λ>
Once you've got this, you can introduce randomness concerns in simpler fashion. And you do not need to involve IO explicitely, as any randomness-friendly monad will do:
shuffleList :: MonadRandom mr => [a] -> mr [a]
shuffleList [] = return []
shuffleList ls =
do
let maxPos = (length ls) - 1
pos <- getRandomR (0, maxPos)
return (funcShuffleList pos ls)
... IO being just one instance of MonadRandom.
You can run the code using the default IO-hosted random number generator:
main = do
let inpList = [0,1,2,3,4,5,6,7,8]::[Integer]
putStrLn $ "inpList = " ++ (show inpList)
-- mr automatically instantiated to IO:
outList1 <- shuffleList inpList
putStrLn $ "outList1 = " ++ (show outList1)
outList2 <- shuffleList outList1
putStrLn $ "outList2 = " ++ (show outList2)
Program output:
$ pickShuffle
inpList = [0,1,2,3,4,5,6,7,8]
outList1 = [6,0,1,2,3,4,5,7,8]
outList2 = [8,6,0,1,2,3,4,5,7]
$
$ pickShuffle
inpList = [0,1,2,3,4,5,6,7,8]
outList1 = [4,0,1,2,3,5,6,7,8]
outList2 = [2,4,0,1,3,5,6,7,8]
$
The output is not reproducible here, because the default generator is seeded by its launch time in nanoseconds.
If what you need is a full random permutation, you could have a look here and there - Knuth a.k.a. Fisher-Yates algorithm.
How to derive a parameterized similarity in a way that it would be convenient to use in Haskell?
The class should be such that the domain can be numeric or text (and possibly something else), and the parameter controlling the internals of comparison function can also be of several types.
Below, you may find the one approach that uses two class parameters. What implications this design entails if the goal is to define several "similarity or equality groups"? (What kind of use cases would be hard to implement compared to some alternative implementation?) In this example, the similarity groups of words could be defined to be edit distances of one, two etc. and in double to be different precisions.
Some of the methods take both numeric and textual inputs like the "quiteSimilar"-method. Why not use just some distance? Some of the similarities should be able to be defined by the user of the parameterized equality, e.g. on text (words) they could be based on synonyms.
And on doubles, well, I don't know yet, what kind of comparisons will be needed. (Suggestions are welcome.) After equalities comes the question, how to compare the order of items so that similar items will be deemed to be equal and not the larger and smaller, see the last line of the output.
{-# LANGUAGE MultiParamTypeClasses #-}
import Data.Array
import qualified Data.Text as T
-- parameterized eq
class Peq a b where peq :: a -> b -> b -> Bool
instance Peq Double Double where peq = almostEqRelPrec
instance Peq Int T.Text where peq = editDistance
class Comment a where
quiteSimilar :: a -> a -> T.Text
instance Comment Double where
quiteSimilar a b = if peq (epsilon * 100::Double) a b then T.pack "alike" else T.pack "unalike"
instance Comment T.Text where
quiteSimilar a b = if peq (1::Int) a b then T.pack "alike" else T.pack "unalike"
x1' x = quiteSimilar 0.25 (0.25 - x * epsilon :: Double)
x1 = quiteSimilar 0.25 (0.25 - 25 * epsilon :: Double)
x2 = quiteSimilar 0.25 (0.25 - 26 * epsilon :: Double)
x3' x = quiteSimilar 1e12 (1e12 - x * ulp 1e12 :: Double)
x3 = quiteSimilar 1e12 (1e12 - 181 * ulp 1e12 :: Double)
x4 = quiteSimilar 1e12 (1e12 - 182 * ulp 1e12 :: Double)
u181 = 181 * ulp 1e12 :: Double
main = do
let a = 0.2 + 0.65 :: Double
b = 0.85 :: Double
s = T.pack "trial"
t = T.pack "tr1al"
putStrLn $ "0.2 + 0.65 = " ++ show a ++ " and compared to " ++ show b ++ ", it is " ++ T.unpack (quiteSimilar a b)
putStrLn $ "Texts " ++ T.unpack s ++ " and " ++ T.unpack t ++ " are " ++ T.unpack (quiteSimilar s t)
putStrLn $ "Note that " ++ show a ++ " > " ++ show b ++ " is " ++ show (a > b)
-- packege Numeric.Limits contains this one
epsilon :: RealFloat a => a
epsilon = r
where r = 1 - encodeFloat (m-1) e
(m, e) = decodeFloat (1 `asTypeOf` r)
ulp :: RealFloat a => a -> a
ulp a = r
where r = a - encodeFloat (m-1) e
(m, e) = decodeFloat (a `asTypeOf` r)
almostEqRelPrec :: (RealFloat a) => a -> a -> a -> Bool
almostEqRelPrec maxRelPrec a b = d <= (largest * maxRelPrec)
where
d = abs $ a - b
largest = max (abs a) (abs b)
editDistance :: Int -> T.Text -> T.Text -> Bool
editDistance i a b = i == editDistance' (show a) (show b)
-- from https://wiki.haskell.org/Edit_distance
-- see also https://hackage.haskell.org/package/edit-distance-0.2.2.1
editDistance' :: Eq a => [a] -> [a] -> Int
editDistance' xs ys = table ! (m,n)
where
(m,n) = (length xs, length ys)
x = array (1,m) (zip [1..] xs)
y = array (1,n) (zip [1..] ys)
table :: Array (Int,Int) Int
table = array bnds [(ij, dist ij) | ij <- range bnds]
bnds = ((0,0),(m,n))
dist (0,j) = j
dist (i,0) = i
dist (i,j) = minimum [table ! (i-1,j) + 1, table ! (i,j-1) + 1,
if x ! i == y ! j then table ! (i-1,j-1) else 1 + table ! (i-1,j-1)]
On my machine, the output is:
0.2 + 0.65 = 0.8500000000000001 and compared to 0.85, it is alike
Texts trial and tr1al are alike
Note that 0.8500000000000001 > 0.85 is True
Edit:
Trying to rephrase the question: could this be achieved more elegantly with a similarity class that has only one parameter a and not two (a and b)? I have a feeling that multiparameter classes may turn out to be difficult later on. Is this a needless fear? First solution along this line that came to my mind was to define similarity class with one parameter a and a class for functions having two parameters. And on instances constraint other type to be similarity class parameter and the other would be for actual method returning Bool.
Are there some benefits of using the latter approach to the one presented? Or actually what are the possible trade-offs between these approaches? And if there are still more ways to make achieve this kind of things, how do they compare?
could this be achieved more elegantly with a similarity class that has only one parameter a and not two (a and b)
Yes. Many MultiParamTypeClasses can be rewritten quite easily to single-param ones... by simply degrading the second parameter to an associated type family:
{-# LANGUAGE TypeFamilies #-}
class Peq b where
type SimilarityThreshold b :: *
peq :: SimilarityThreshold b -> b -> b -> Bool
instance Peq Double where
type SimilarityThreshold Double = Double
peq = almostEqRelPrec
instance Peq T.Text where
type SimilarityThreshold T.Text = Int
peq = editDistance
This is quite a bit more verbose, but indeed I tend to favour this style. The main difference is that the associated type family always assigng each type of values to be compared unambiguously a threshold-type. This can save you some could not deduce... type inference trouble, however it also means that you can't use two different metric-types for a single type (but why would you, anyway).
Note that you can achieve exactly the same semantics by simply adding a fundep to your original class:
{-# LANGUAGE FunctionalDependencies #-}
class Peq a b | b -> a where
peq :: a -> b -> b -> Bool
This is just a bit different in usage – again I tend to favour the type families approach: it is more explicit in what the parameters are for, while at the same time avoiding the second parameter to turn up in the constraints to any Peq-polymorphic function.
Below I have defined a function that converts a list of base-3 digits to the corresponding numerical value. For example:
f "201" = (2 * 3^2) + (0 * 3^1) + (1 * 3^0) = 19
f "12" = 5
f "1202" = 47
f "120221" = 430
Here is a definition using comprehension:
f :: String -> Int
f str = sum (listToFinal (stringToTuples str))
Helper functions:
-- 1) converts "201" to "102"
reverse "str"
-- 2) converts "102" to 102
stringToInt :: String -> Int
stringToInt str = read str :: Int
-- 3) converts 102 to ['1','0','2']
intToList :: Int -> [Int]
intToList 0 = []
intToList x = intToList (x `div` 10) ++ [x `mod` 10]
-- 4) converts "201" to [(1,0),(0,1),(2,2)] using reverse, stringToInt, intToList
stringToTuples :: String -> [(Int,Int)]
stringToTuples str = zip (intToList (stringToInt (reverse str))) [0..]
-- 5) converts [(1,0),(0,1),(2,2)] to [1*3^0, 0*3^1, 2*3^2]
listToFinal :: [(Int,Int)] -> [Int]
listToFinal list = [ x * (3^y) | (x,y) <- list ]
Now I'd like to do it with recursion only (well, using basic & library functions too, of course).
An idea: I was thinking of taking the head of each element in the list and simply multiplying it with 3^(length of string - 1). The only problem is, with each recursive call the power of three would have to decrease by 1, e.g. given:
recursive_version "201" = (2 * 3^2) + (0 * 3^1) + (1 * 3^0)
How to implement this?
Here is a much simpler approach; note that, through the use of foldl, it's only "implicitly" recursive, though. For information, digitToInt is exported by Data.Char.
import Data.Char
import Data.List ( foldl' )
--- horner x xs : the value of polynomial 'xs' at point 'x'
horner :: Int -> [Int] -> Int
horner x = foldl' (\c1 c0 -> c1 * x + c0) 0
-- f s : the integer whose representation in base 3 is string 's'
f :: String -> Int
f = horner 3 . map digitToInt
When you define it recursively, the natural way to decrement the length is trimming the array from the head. For example:
base3 x = base3' x 0 where
base3' (d:ds) v = base3' ds $ v + d * 3 ^ length ds
base3' [] v = v
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 11 years ago.
Hey, I'm really new to Haskell and have been using more classic programming languages my whole life. I have no idea what is going on here. I'm trying to make a very simple Viterbi algorithm implementation, but for only two states (honest and dishonest casino)
I have a problem where I want to address my array, but I don't think I'm getting types right. That or I'm making a new array each time I try to address it - equally stupid. Look at myArray, te infix, and dynamicProgram especially, PLEASE. Pretty pretty please
Code
import Array
import Char
trans :: Int -> Int -> Double -> Double -> Double
trans from x trans11 trans21 =
if (from == 1) && (x == 1)
then trans11
else if (from == 1) && (x == 2)
then (1-trans11)
else if (from == 2) && (x == 1)
then trans21
else (1-trans21)
em :: Char -> [Double] -> Double
em c list = list!! a
where a = digitToInt c
intToChar :: Int -> Char
intToChar n | n == 1 = '1'
| n == 2 = '2'
casino :: Char -> Int -> Int -> [Double] -> [Double] -> Double -> Double -> Double
casino seqchar 1 y em1 em2 t1 t2= 0.5 * (em seqchar em1)
casino seqchar 2 y em1 em2 t1 t2= 0.5 * (em seqchar em2)
casino seqchar x y em1 em2 t1 t2= maximum[ (1 ## y-1)*(em seqchar em1)*(trans 1 x t1 t2),(2 ## y-1)*(em seqchar em2)*(trans 2 x t1 t2) ]
dynamicProgram :: [Char] -> (Char -> Int -> Int -> [Double] -> [Double] -> Double -> Double -> Double) -> [Double] -> [Double] -> Double -> Double -> (Array a b)
dynamicProgram string score list1 list2 trans11 trans21 = myArray 1 len
[score (string!!y) x y list1 list2 trans11 trans21 | x Int -> [Double] -> Array a b
myArray startIndex endIndex values = listArray (startIndex,startIndex) (endIndex,endIndex) values
traceback :: [Char] -> Int -> Int -> [Double] -> [Double] -> Double -> Double -> [Char]
traceback s 1 0 em1 em2 t1 t2 = []
traceback s 2 0 em1 em2 t1 t2 = []
traceback s x y em1 em2 t1 t2 | x##y == (1 ## y-1)*(em (s!!y) em1)*(trans 1 x t1 t2) = '1' : traceback s 1 (y-1) em1 em2 t1 t2
| x##y == (2 ## y-1)*(em (s!!y) em1)*(trans 2 x t1 t2) = '2' : traceback s 2 (y-1) em1 em2 t1 t2
answer :: [Char] -> [Double] -> [Double] -> Double -> Double -> [Char]
answer string list1 list2 t1 t2 = reverse $ maxC : traceback string max end list1 list2 t1 t2 $ dynamicProgram casino string list1 list2 t1 t2
where
end = (length string) + 1
max | maximum (1##end) (2##end) == 1##end = 1
| maximum (1##end) (2##end) == 2##end = 2
maxC = intToChar max
infix 5 ##
(##) i j = myArray ! (i, j)
main = do
putStrLn "What is the sequence to test?"
seq state 1 transmission probability?"
trp1 state 2 transmission probability is " ++ (1-trp1)
putStrLn "What is the state 2 -> state 1 transmission probability?"
trp2 state 2 transmission probability is " ++ (1-trp2)
putStrLn "I assume that the prob of starting in either state is 1/2. Go!"
answer seq st1 st2 trp1 trp2
I copied the code from the edit window (something in stackoverflow's parser is eating part of the code) and tried it on ghci, which found several errors. The first error was:
foo.hs:34:71:
Couldn't match expected type `[e]' against inferred type `(a, b)'
In the second argument of `listArray', namely
`(endIndex, endIndex)'
In the expression:
listArray (startIndex, startIndex) (endIndex, endIndex) values
In the definition of `myArray':
myArray startIndex endIndex values
= listArray (startIndex, startIndex) (endIndex, endIndex) values
The type of listArray is:
listArray :: (Ix i) => (i, i) -> [e] -> Array i e
-- Defined in GHC.Arr
It takes a tuple with the lower and upper bounds and the list. So, the correct expression would probably be:
listArray (startIndex, endIndex) values
And the type of myArray is not Array a b, it is Array Int Double.
The second error was:
foo.hs:43:44:
Couldn't match expected type `a -> b'
against inferred type `[Char]'
In the first argument of `($)', namely
`maxC : (traceback string max end list1 list2 t1 t2)'
In the second argument of `($)', namely
`(maxC : (traceback string max end list1 list2 t1 t2))
$ (dynamicProgram casino string list1 list2 t1 t2)'
In the expression:
reverse
$ ((maxC : (traceback string max end list1 list2 t1 t2))
$ (dynamicProgram casino string list1 list2 t1 t2))
$ is right associative, so the rightmost $ is looked at first. The first parameter to it must be a function, which it will call with its rightmost parameter as the argument. Here, however, it is a list.
The third error was:
foo.hs:51:11:
Couldn't match expected type `Array i e'
against inferred type `Int -> Int -> [Double] -> Array a b'
In the first argument of `(!)', namely `myArray'
In the expression: myArray ! (i, j)
In the definition of `##': ## i j = myArray ! (i, j)
myArray is not an array; it is a function which takes three parameters and constructs an array based on them.
Here probably your being used to more traditional imperative languages is tripping you. In a traditional imperative language, it would be natural to have a global myArray variable which you then can access from the middle of your program. In Haskell however, absent more advanced trickery which you should not try while you are a beginner, a "global" variable is more like a constant value (which is lazily computed on first use, but which as far as you care could have been computed by the compiler while generating your executable). You cannot initialize it from values you read as input.
Your best way around that is to pass the array through the program, which unfortunately will need several changes and negates the usefulness of your ## operator. You can hide the passing of the array in several more advanced ways, but while learning it is best to be more explicit.
The last error was:
foo.hs:63:4:
Couldn't match expected type `[a]' against inferred type `IO ()'
In the first argument of `(++)', namely
`putStrLn
"I assume that the state 1 -> state 2 transmission probability is "'
In the expression:
(putStrLn
"I assume that the state 1 -> state 2 transmission probability is ")
++
(1 - trp1)
In a 'do' expression:
(putStrLn
"I assume that the state 1 -> state 2 transmission probability is ")
++
(1 - trp1)
This has two errors: the one the compiler complained about is a precedence problem, as the compiler-added parenthesis readly show, and which can easily be fixed by correct application of either parenthesis or the $ operator. The other error, which you will find after you fix this one, is that you cannot concatenate a string and a number; you have to convert the number to a string.
This was all without looking at the algorithm or even most of the code, just looking at the compiler errors. If you want a two-dimensional array, for instance, the correct expression for the first error would be:
listArray ((startIndex, startIndex), (endIndex, endIndex)) values
Now both bounds are tuples, and its type is Array (Int, Int) Double.
You can rewrite the trans-function like this:
trans :: Int -> Int -> Double -> Double -> Double
trans 1 1 trans11 trans21 = trans11
trans 1 2 trans11 trans21 = 1-trans11
trans 2 1 trans11 trans21 = trans21
trans _ _ trans11 trans21 = 1-trans21