【问题标题】:Redesign of Haskell type classes重新设计 Haskell 类型类
【发布时间】:2013-04-02 14:34:59
【问题描述】:

在获得一些帮助后,了解了我在尝试编译代码时遇到的问题,在这个问题 (Trouble understanding GHC complaint about ambiguity) 中,Will Ness 建议我重新设计我的类型类以避免我不完全满意的解决方案。

有问题的类型类是这些:

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

我正在尝试在 Haskell 中创建一个可扩展的进化算法,它应该支持不同的 GenomesPhenotypes。例如,一个Genome 可以是一个位数组,另一个可以是一个整数列表,而Phenotypes 也将不同于http://en.wikipedia.org/wiki/Colonel_Blotto 中代表部队移动的双精度列表,或者它可以代表一个ANN。

由于Phenotype 是从Genome 开发的,因此使用的方法必须是可以互换的,并且一个Genome 类应该能够通过提供不同的开发方法来支持多个Phenotypes(这可以静态完成在代码中,并且不必在运行时动态完成)。

在大多数情况下,使用这些类型类的代码应该完全不知道所使用的特定类型,这就是我提出上述问题的原因。

我想适应这些类型类的一些代码是:

-- |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

我知道必须更改此代码并添加新的约束,但我想展示一些我想到的使用类型类的代码。例如,上面的全代替换不需要了解底层Genome 的任何信息即可正常运行;它只需要知道Phenotypes 可以产生产生它的Genome,这样它就可以将它们一起繁殖并创造新的孩子。 fullGenerational 的代码应尽可能通用,这样一旦设计了新的Phenotype 或创建了更好的Genome,就无需更改。

如何更改上述类型类以避免我在类型类歧义方面遇到的问题,同时在通用 EA 代码中保留我想要的属性(应该是可重用的)?

【问题讨论】:

  • 将类型类转换为等效字典:参见thisthis
  • @GabrielGonzalez 嗯,我可能会误解,但这将如何帮助解决我遇到的歧义问题?我知道样式可能更简洁,但我不明白将我已有的内容转换为数据声明将如何解决问题。
  • 对不起,我误解了你所说的模棱两可的意思。无视我。
  • 很棒的工作顺便说一句,如此详细地描述了你的问题,真的让它更容易处理。我希望关于 SO 的每个问题都是这样的! :)

标签: haskell typeclass evolutionary-algorithm


【解决方案1】:

"它只需要知道Phenotypes可以产生产生它的基因组"

这意味着表型实际上是两种类型的关系,另一种是用于产生给定表型的基因组类型:

{-# 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

这编译。 genome 的每个表型 will have to provide exactly one implementation

【讨论】:

  • 这似乎是我正在寻找的。此答案中唯一缺少的是您之前在 cmets 中提供的链接(haskell.org/haskellwiki/Functional_dependencies
  • @Nordmoen 顺便说一句,我不记得这里使用的语言 pragma 的确切拼写。我刚刚尝试将文件加载到 GHCi 中,它告诉我缺少哪些选项。
  • @Nordmoen BTW 与 Phenotype 类定义中的 Genome b 约束,两个函数中的约束 Genome a 是多余的,可以删除。但这并没有什么坏处,也许还有其他文档用途。
猜你喜欢
  • 2023-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-05
  • 1970-01-01
  • 1970-01-01
  • 2020-05-23
相关资源
最近更新 更多