[英]Redesign of Haskell type classes
After getting some help, understanding the problem I had trying to compile the code, in this question ( Trouble understanding GHC complaint about ambiguity ) Will Ness suggested I redesign my type classes to avoid a solution I was not completely happy with. 在得到一些帮助后,理解我试图编译代码的问题,在这个问题中( 麻烦理解GHC关于模糊性的抱怨 )Ness会建议我重新设计我的类型类以避免我不满意的解决方案。
The type classes in question are these: 有问题的类型是这些:
class (Eq a, Show a) => Genome a where
crossover :: (Fractional b) => b -> a -> a -> IO (a, a)
mutate :: (Fractional b) => b -> a -> IO a
develop :: (Phenotype b) => a -> b
class (Eq a, Show a) => Phenotype a where
--In case of Coevolution where each phenotype needs to be compared to every other in the population
fitness :: [a] -> a -> Int
genome :: (Genome b) => a -> b
I'm trying to create an extendible evolutionary algorithm in Haskell which should support different Genomes
and Phenotypes
. 我正在尝试在Haskell中创建一个可扩展的进化算法,它应该支持不同的
Genomes
和Phenotypes
。 For instance one Genome
could be a bit array, another could be a list of ints, and the Phenotypes
will also be diverse from just a list of doubles representing troop movement in http://en.wikipedia.org/wiki/Colonel_Blotto , or it could represent an ANN. 例如,一个
Genome
可能是一个阵列,另一个可能是一个整数列表, Phenotypes
也将不同于一个代表部队运动的双重列表http://en.wikipedia.org/wiki/Colonel_Blotto ,或者它可以代表人工神经网络。
Since a Phenotype
is developed from a Genome
the methods used must be quite interchangeable, and one Genome
class should be able to support multiple Phenotypes
by supplying a different develop method (this can be done statically in code, and does not have to be done dynamically at runtime). 由于
Phenotype
是从Genome
开发的,所使用的方法必须是完全可互换的,并且一个Genome
类应该能够通过提供不同的开发方法来支持多个Phenotypes
(这可以在代码中静态完成,而不必动态完成)在运行时)。
The code using these type classes should, for the most part, be blissfully unaware of the specific types used, which is what lead me to ask the question mentioned above. 在大多数情况下,使用这些类型类的代码应该幸福地不知道所使用的特定类型,这就是让我提出上述问题的原因。
Some of the code that I want to adapt to these type classes are: 我想要适应这些类型类的一些代码是:
-- |Full generational replacement selection protocol
fullGenerational :: (Phenotype b) =>
(Int -> [b] -> IO [(b, b)]) -> --Selection mechanism
Int -> --Elitism
Int -> --The number of children to create
Double -> --Crossover rate
Double -> --Mutation rate
[b] -> --Population to select from
IO [b] --The new population created
fullGenerational selection e amount cross mute pop = do
parents <- selection (amount - e) pop
next <- breed parents cross mute
return $ next ++ take e reverseSorted
where reverseSorted = reverse $ sortBy (fit pop) pop
breed :: (Phenotype b, Genome a) => [(b, b)] -> Double -> Double -> IO [b]
breed parents cross mute = do
children <- mapM (\ (dad, mom) -> crossover cross (genome dad) (genome mom)) parents
let ch1 = map fst children ++ map snd children
mutated <- mapM (mutate mute) ch1
return $ map develop mutated
I understand that this code will have to be changed and new constraints will have to be added, but I wanted to show some of the code I have in mind using the type classes with. 我知道必须更改此代码并且必须添加新的约束,但我想要使用类型类来显示我想到的一些代码。 For instance, the full generational replacement above does not need to know anything about the underlying
Genome
to function properly; 例如,上面的完整世代替代品不需要知道任何关于底层
Genome
正常运作; it only needs to know that Phenotypes
can produce the Genome
that produced it so that it can breed them together and create new children. 它只需要知道
Phenotypes
可以产生产生它的Genome
,以便它可以将它们一起繁殖并创造新的孩子。 The code for fullGenerational
should be as general as possible so that once a new Phenotype
is designed or a better Genome
is created, it does not need to be changed. fullGenerational
的代码应尽可能通用,这样一旦设计了新的Phenotype
或创建了更好的Genome
,就不需要更改它。
How can the type classes above be changed to avoid the problems I was/am having with type class ambiguity while retaining the properties I want in the general EA code (which should be reusable)? 如何更改上面的类型类以避免我遇到类型类歧义的问题,同时在一般EA代码中保留我想要的属性(应该是可重用的)?
"it only needs to know that Phenotypes can produce the Genome which produced it "
“它只需要知道表型可以产生产生它的基因组”
this means that Phenotype is really a relation on two types, the other being a Genome type used to produce a given Phenotype: 这意味着Phenotype实际上是两种类型的关系,另一种是用于产生给定表型的基因组类型:
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
import Data.List (sortBy)
class (Eq a, Show a) => Genome a where
crossover :: (Fractional b) => b -> a -> a -> IO (a, a)
mutate :: (Fractional b) => b -> a -> IO a
develop :: (Phenotype b a) => a -> b
class (Eq a, Show a, Genome b) => Phenotype a b | a -> b where
-- In case of Coevolution where each phenotype needs to be compared to
-- every other in the population
fitness :: [a] -> a -> Int
genome :: a -> b
breed :: (Phenotype b a, Genome a) => [(b, b)] -> Double -> Double -> IO [b]
breed parents cross mute = do
children <- mapM (\(dad, mom)-> crossover cross (genome dad) (genome mom))
parents
let ch1 = map fst children ++ map snd children
mutated <- mapM (mutate mute) ch1
return $ map develop mutated
-- |Full generational replacement selection protocol
fullGenerational :: (Phenotype b a, Genome a) =>
(Int -> [b] -> IO [(b, b)]) -> --Selection mechanism
Int -> --Elitism
Int -> --The number of children to create
Double -> --Crossover rate
Double -> --Mutation rate
[b] -> --Population to select from
IO [b] --The new population created
fullGenerational selection e amount cross mute pop = do
parents <- selection (amount - e) pop
next <- breed parents cross mute
return $ next ++ take e reverseSorted
where reverseSorted = reverse $ sortBy (fit pop) pop
fit pop a b = LT -- dummy function
This compiles. 这编译。 Each Phenotype will have to provide exactly one implementation of
genome
. 每种表型都必须提供一种
genome
实现 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.