由do语句生成的嵌套列表

时间:2017-02-09 13:35:26

标签: haskell random monads

作为以下代码的一部分,我有两个人口' (遗传算法)。每个人口都包含许多部分解决方案。结合每个群体的部分解决方案会产生一个完整的解决方案,我正试图计算一个适应度。分配给每个人的适应度最初是通过将其与相反人群中的随机部分解匹配来计算的:

generateInitial :: (MonadRandom m)
                => EvolutionParameters
                -> Domain
                -> (Domain -> X -> Y -> Float)
                -> m ([(X, Float)], [(Y, Float)])
generateInitial ep@(EvolutionParameters popSize _ _ _) d fitnessFunction = do
  (xs, ys) <- genPopulations popSize
  (
      initialFitnessXs ep d fitnessFunction (xs, ys)
    , initialFitnessYs ep d fitnessFunction (xs, ys)
  )

-- Returns: m [(X, Float)]
initialFitnessXs ep d@(Domain cs ts _) fnf (xs, ys) = do
  yPicks <- replicateM (length ys) (pick ys)
  return $ map (\(x, y) -> (x, fnf d x y)) $ zip xs yPicks

-- Returns: m [(Y, Float)]
initialFitnessYs ep d@(Domain cs ts _) fnf (xs, ys) = do
  xPicks <- replicateM (length xs) (pick xs)
  return $ map (\(x, y) -> (p, fnf d x y)) $ zip xPicks ys

但是,从generateInitial返回的类型不是我想象的m ([(Xs, Float)], [(Ys, Float)]),而是将列表深化,导致:

• Couldn't match type ‘[(X, Float)]’
                 with ‘(X, Float)’
  Expected type: m ([(X, Float)], [(Y, Float)])
    Actual type: m ([[(X, Float)]], [[(Y, Float)]])

是什么导致了这种进一步的嵌套,我怎么能避免它?

2 个答案:

答案 0 :(得分:1)

让我们从这里开始:

-- Returns: m [(X, Float)]
initialFitnessXs ep d@(Domain cs ts _) fnf (xs, ys) = do

(这应该有自己的类型注释)

由于m是普遍量化的,我们可以选择m = []并让initialFitnessXs返回[[(X, Float)]]

然后,

  (
      initialFitnessXs ep d fitnessFunction (xs, ys)
    , initialFitnessYs ep d fitnessFunction (xs, ys)
  )

将制作一对列表清单。

你可能想要

      x <- initialFitnessXs ep d fitnessFunction (xs, ys)
      y <- initialFitnessYs ep d fitnessFunction (xs, ys)
      return (x,y)

或使用适用的符号

      (,) <$> initialFitnessXs ep d fitnessFunction (xs, ys)
          <*> initialFitnessYs ep d fitnessFunction (xs, ys)

这将产生所有(x,y)组合。 也许您想要zip这些列表,但很难告诉我。

答案 1 :(得分:1)

  • TL; DR; ---供应类型签名来解决这类问题。如果您不确定签名,请使用类型孔。在个别条款上使用类型签名有助于缩小问题范围。

我将介绍一种使用类型系统和编译器来帮助直接调试的技术。所以,虽然你已经有一个简洁的答案可以解决你的问题,但我希望你也能发现它很有用。

此方法依赖于类型签名中使用类型孔_(在GHC中自版本???)。当GHC遇到这样的漏洞时,它会告诉我们预期的类型,允许你使用类型系统来解决这种类型(问题)。

获取错误

为了提供帮助,最好有(几乎)工作的例子。就目前而言,开始编译已发布的代码需要很多。但是,由于这是一个关于不理解返回什么类型的问题,所以达到几乎工作状态的技术是类似的,所以我在这里包含它。

我打开我的编辑器(我使用Leksah btw)启动一个新模块并粘贴给定的代码。首先,我解决的编译投诉是进口。

import Control.Monad.Random
import Control.Monad(replicateM)

接下来,我收到有关不匹配括号的错误,毫无疑问,return的最后一行包含generateInitial。然后我看到许多无法识别的类型,所以我为它们制作了一些存根,知道我可能必须在我去的时候改进它们的定义。

data EvolutionParameters = EvolutionParameters PopSize EP2 EP3 EP4
data Domain = Domain Cs Ts D3
data X = X
data Y = Y

我看到EvolutionParametersDomain构造函数接受了参数,所以我给它们一些参数,当在函数中指定它们时使用绑定变量名称,否则只是随机名称

data PopSize = PopSize
data EP2 = EP2
data EP3 = EP3
data EP4 = EP4
data Cs = Cs
data Ts = Ts
data D3 = D3

接下来,我们需要一些缺少的功能。我使用GHC告诉我的类型定义了这些。

genPopulations :: PopSize -> m ([X],[Y])
genPopulations = undefined

pick :: t a -> m b
pick = undefined

解决错误

现在出现最大错误:

 Couldn't match type ‘[(X, Float)]’ with ‘(X, Float)’
      Expected type: m ([(X, Float)], [(Y, Float)])
        Actual type: m ([[(X, Float)]], [[(t0, Float)]])

AWSOME!现在回答实际问题。

首先,我想看看丢失的签名会发生什么,也许他们没有返回预期的内容。我使用类型孔指定它们的类型:

initialFitnessXs :: _ 

initialFitnessYs :: _ 

此外,还有一个未指定的变量p,我假设它是y的拼写错误。编译器可以帮助我们。它告诉我这些洞中的预期,并允许我稍微填写签名。

initialFitnessXs :: t -> Domain -> (Domain -> t2 -> t3 -> t1)
                 -> ([t2],[a]) -> [[(t2,t1)]]

initialFitnessYs :: t -> Domain -> (Domain -> t2 -> t3 -> t1)
                 -> ([a],[t3]) -> [[(t3,t1)]]

嗯,从评论中我们似乎期待m [(X,Float)]的回归,让我们强迫一下。在此过程中,编译器指示我与t2交换X,并与t3交换a。这导致以下签名。

initialFitnessXs :: (MonadRandom m)
                 => t -> Domain -> (Domain -> X -> a -> Float)
                 -> ([X],[a]) -> m [(X,Float)]

initialFitnessYs :: (MonadRandom m)
                 => t -> Domain -> (Domain -> a -> Y -> Float)
                 -> ([a],[Y]) -> m [(Y,Float)]

接下来,我回到实际错误的来源,现在的行是:

return ( initialFitnessXs ep d fitnessFunction (xs, ys)
       , initialFitnessYs ep d fitnessFunction (xs, ys)
       )

我回头看看错误和这一行。我们为initialFitnessXinitialFitnessY提供了特定的签名,因此虽然它没有改变,但我们可以确定问题不在于返回这些函数。

既然我们已经有了Chi的答案,那么应该清楚做什么。假装我不知道,我再次使用漏洞:

return ( initialFitnessXs ep d fitnessFunction (xs, ys) :: _
       , initialFitnessYs ep d fitnessFunction (xs, ys) :: _
       )

相应的错误:

• Found type wildcard ‘_’ standing for ‘m1 [(X, Float)]’
  Where: ‘m1’ is a rigid type variable bound by
           the inferred type of
           <expression> :: MonadRandom m1 => m1 [(X, Float)]
           at src/GeneticAlg.hs:41:14

如果这些术语具有这些类型,则应重写generateInitial签名。

generateInitial :: (MonadRandom m)
                => EvolutionParameters
                -> Domain
                -> (Domain -> X -> Y -> Float)
                -> m (m [(X, Float)], m [(Y, Float)])

成功!好吧,至少编译。希望通过这些步骤可以明显解决问题所在。仍然需要解决问题,但希望您现在可以看到使用编译器的一种方法。它可以帮助您比较您所期望的与您所写的内容。通过询问个别条款的签名,您可以缩小不匹配的位置。

如果您不知道该怎么做,也可以使用Hoogle.com。例如,虽然hoogling m (m a,m a) -> m (a,a)没有返回任何内容,但搜索

m (m a) -> m a

从Control.Monad返回join,这可能会导致您需要某种Monad操作。当然,这并不明显。

Typed Holes Wiki