Haskell中的动态列表理解

时间:2013-02-14 11:02:23

标签: haskell

假设我有一个列表推导,它返回一个序列列表,其中所选择的元素彼此依赖(参见下面的示例)。有没有办法(方便地)根据早期计算编程元素的数量及其相关条件?例如,返回类型[[a,b,c]]或[[a,b,c,d,e]]取决于程序中的另一个值?此外,还有其他/更好的方式而不是列表理解来制定相同的想法吗?

(我觉得可能,尽管很麻烦且有限,但要写出一个更大的列表理解来开始并通过添加 s 参数和辅助函数来修剪它,这可能会使一个或多个元素一个值可以在以后轻松过滤,相关条件默认为True。)

s = [[a, b, c, d] | a <- list, someCondition a, 
                    b <- list, b /= a, not (someCondition b), 
                    otherCondition a b,
                    c <- list, c /= a, c /= b, not (someCondition c),
                    otherCondition b c,
                    d <- list, d /= a, d /= b, d /= c,
                    someCondition d, someCondition (last d),
                    otherCondition c d]

4 个答案:

答案 0 :(得分:4)

这个问题难以理解。

  

是否有办法(方便地)根据较早的计算对元素数量及其相关条件进行编程?

问题是“程序”在这句话中实际上不是一个可理解的动词,因为人类编程计算机,或编程录像机,但你不能“编程编号”。所以我不明白你在这里想说的是什么。

但我可以给你代码审查,也许通过代码审查,我可以理解你提出的问题。

未经请求的代码审核

听起来你正试图通过消除死胡同来解决迷宫。

您的代码实际执行的是:

  1. 生成一个非死角或死角附近的单元格列表,名为filtered

  2. 从步骤1生成一系列相邻单元格,sequences

  3. 将四个这样的相邻序列连接成一条路线。

  4. 主要问题:这只有在正确的路线恰好是八个瓷砖长时才有效!试着解决这个迷宫:

    [E]-[ ]-[ ]-[ ] 
                 |
    [ ]-[ ]-[ ]-[ ]
     |
    [ ]-[ ]-[ ]-[ ]
                 |
    [ ]-[ ]-[ ]-[ ]
     |
    [ ]-[ ]-[ ]-[E]
    

    因此,从代码审查中倒退,听起来像你的问题是:

      

    如果我事先不知道它有多长,我如何生成一个列表?

    解决方案

    您可以通过搜索解决迷宫(DFS,BFS,A *)。

    import Control.Monad
    
    -- | Maze cells are identified by integers
    type Cell = Int
    
    -- | A maze is a map from cells to adjacent cells
    type Maze = Cell -> [Cell]
    
    maze :: Maze
    maze = ([[1],     [0,2,5],     [1,3],   [2],
             [5],     [4,6,1,9],   [5,7],   [6,11],
             [12],    [5,13],      [9],     [7,15],
             [8,16],  [14,9,17],   [13,15], [14,11],
             [12,17], [13,16,18],  [17,19], [18]] !!)
    
    -- | Find paths from the given start to the end
    solve :: Maze -> Cell -> Cell -> [[Cell]]
    solve maze start = solve' [] where
      solve' path end =
        let path' = end : path
        in if start == end 
           then return path'
           else do neighbor <- maze end
                   guard (neighbor `notElem` path)
                   solve' path' neighbor
    

    函数solve通过深度优先搜索工作。它不是将所有内容都放在一个列表中,而是递归地工作。

    1. 要查找从startend的路径,如果start /= end

    2. 查看与结尾相邻的所有单元格neighbor <- maze end

    3. 确保我们不会回溯单元格guard (negihbor `notElem` path)

    4. 尝试查找从startneighbor的路径。

    5. 不要试图立刻理解整个函数,只要理解递归的一点。

      摘要

      如果你想找到从单元格0到单元格19的路径,请说明:我们知道单元格18和19是连接的(因为它们是直接连接的),所以我们可以尝试解决从中查找路径的问题单元格0到单元格18。

      这是递归。

      脚注

      守卫,

      someCondition a == True
      

      相当于,

      someCondition a
      

      因此也相当于,

      (someCondition a == True) == True
      

      或者,

      (someCondition a == (True == True)) == (True == (True == True))
      

      或者,

      someCondition a == (someCondition a == someCondition a)
      

      第一个,someCondition a,很好。

      关于do符号

      的脚注

      上例中的do符号等同于列表推导,

      do neighbor <- maze end
         guard (neighbor `notElem` path)
         solve' path' neighbor
      

      列表推导语法中的等效代码是

      [result | neighbor <- maze end,
                neighbor `notElem` path,
                result <- solve' path' neighbor]
      

答案 1 :(得分:2)

  

有没有办法(方便地)根据较早的计算编程元素的数量及其相关条件?例如,返回类型[[a,b,c]]或[[a,b,c,d,e]]取决于程序中的另一个值?

我想你想在类型签名中静态编码列表(或向量)的长度。无法在类型级别检查标准列表的长度。

One approach这样做是为了使用phantom types,并引入将编码不同大小的虚拟数据类型:

newtype Vector d = Vector { vecArray :: UArray Int Float }

-- using EmptyDataDecls extension too
data D1
data D2
data D3

现在你可以创建不同长度的矢量,这些矢量将具有不同的类型:

vector2d :: Float -> Float -> Vector D2
vector2d x y = Vector $ listArray (1,2) [x,y]

vector3d :: Float -> Float -> Float -> Vector D3
vector3d x y z = Vector $ listArray (1,3) [x,y,z]

如果输出的长度取决于输入的长度,则考虑使用type-level arithmetics来参数化输出。 你可以通过谷歌搜索“Haskell静态大小的矢量”找到更多。

更简单的解决方案是使用 固定长度的元组。如果你的函数可以产生3元组或5元组,用Either数据类型包装它们:`(a,b,c)(a,b,c,d,e)。

答案 2 :(得分:1)

看起来你正试图通过有限域中的唯一选择来解决一些逻辑谜题。请参考以下内容:

这有助于我们的方式,我们在从中挑选时带着我们的域名;下一个选择是从包含前一个选择之后剩下的内容的缩小域中制作的,因此链是自然形成的。 E.g。

p43 = sum [ fromDigits [v0,v1,v2,v3,v4,v5,v6,v7,v8,v9]
            | (dom5,v5) <- one_of [0,5] [0..9]   -- [0..9] is the
            , (dom6,v6) <- pick_any dom5         --   initial domain
            , (dom7,v7) <- pick_any dom6          
            , rem (100*d5+10*d6+d7) 11 == 0 
            ....

-- all possibilities of picking one elt from a domain
pick_any :: [a] -> [([a], a)]
pick_any []     = [] 
pick_any (x:xs) = (xs,x) : [ (x:dom,y) | (dom,y) <- pick_any xs]

-- all possibilities of picking one of provided elts from a domain
--                           (assume unique domains, i.e. no repetitions)
one_of :: (Eq a) => [a] -> [a] -> [([a], a)]
one_of ns xs = [ (ys,y) | let choices = pick_any xs, n <- ns,
                          (ys,y) <- take 1 $ filter ((==n).snd) choices ]

作为列表理解的一部分,您可以轻松检查答案中的多个元素:

s = [answer | a <- .... , let answer=[....] , length answer==4 ]

或根据条件创建不同的答案,

s = [answer | a <- .... , let answer=if condition then [a,b,c] else [a]]

答案 3 :(得分:1)

您有Data.List.subsequences

您可以用monadic形式编写列表理解(参见guards in Monad Comprehensions):

(解释:monad必须是支持失败的MonadPlus实例。

guard False使monad无法评估为mzero。后续结果附加了List monad的mplus =(++)。)

import Control.Monad (guard)

myDomain = [1..9]   -- or whatever

validCombinations :: [a] -> [[a]]
validCombinations domainList = do
        combi <- List.subsequences domainList
        case combi of
                [a,b] -> do
                        guard (propertyA a && propertyB b)
                        return combi

                [a,b,c] -> do
                        guard (propertyA a && propertyB b && propertyC c)
                        return combi

                _ -> guard False

main = do
         forM_ (validCombinations myDomain) print

再次更新,递归获取元素,保存组合和检查

import Control.Monad

validCombinations :: Eq a => Int -> Int -> [a] -> [(a -> Bool)] -> [a] -> [[a]]
validCombinations indx size domainList propList accum = do

    elt <- domainList   -- try all domain elements

    let prop = propList!!indx
    guard $ prop elt               -- some property
    guard $ elt `notElem` accum    -- not repeated 

    {-
    case accum of
        prevElt : _ -> guard $ some_combined_check_with_previous elt prevElt
        _ -> guard True
        -}

    if size > 1 then do
         -- append recursively subsequent positions

         other <- validCombinations (indx+1) (size-1) domainList propList (elt : accum)

         return $ elt : other
    else
         return [elt]

myDomain = [1..3] :: [Int]

myProps = repeat (>1)

main = do
           forM_ (validCombinations 0 size myDomain myProps []) print 
   where
      size = 2 

大小为2的结果具有非常重要的结果:

    [2,3]
    [3,2]