【问题标题】:All combinations of two ranges as a list of tuples of two indices两个范围的所有组合作为两个索引的元组列表
【发布时间】:2021-10-03 07:53:16
【问题描述】:

例如,给定一个元组 (2, 3) 作为参数, 它应该返回[(0,0), (0,1), (0,2), (1,0), (1,1), (1,2)], 虽然它可以是任何顺序。

这是我想出的代码,但是它没有返回正确的结果,谁能提供更好的解决方案或想法?

getAll :: (Int, Int) —> [(Int,Int)] 
getAll (x,y) 
  | x == 0 && y == 0 = [(0,0)] 
  | x == 1 && y == 1 = [(1,1)] 
  | 0 + 1 < x && 0 + 1 < y 
                     = (0,0) : getAll (x+1, y+1) 

更新:

getAll :: (Int, Int) -> [(Int,Int)]
getAll (x,y) = case (x,y) of
        (0,0) -> [(0,0)]
        (1,1) -> [(0,0)]
        (0, y) -> (0, y-1) : getAll (0, y-1)
        (x, 0) -> (x-1, 0) : getAll (x-1, 0)    
                                                             

我尝试将它计算为两种情况,但我有一个问题,例如当我输入(0,3) 时,它会输出[(0,2), (0,1), (0,0), (0,0)]。我知道为什么会输出两次(0,0),但是我不知道怎么解决,你能看看我的代码,看看问题出在哪里?

【问题讨论】:

  • 你好,我只是想问一下如何将代码发布为文本,这是我第一次发布问题。谢谢
  • 您只需输入代码。 (我已经为你做了)。
  • 我可以向您展示一个使用列表理解来解决此问题的单行程序。但也许你必须自己用递归来解决它?
  • 谢谢你帮我改。如果您能告诉我如何以单线性方式编写它,那就太好了。另外,我重写了我的代码,不知道上面的代码怎么改?
  • 点击帖子下方的"edit",上面写着“分享编辑关注”。

标签: list haskell tuples range combinations


【解决方案1】:

我能想到的一种简洁的实现方式是使用列表推导:

getAll :: (Int, Int) -> [(Int,Int)]
getAll (x, y) = [(a, b) | a <- [0..x-1], b <- [0..y-1]]

如果你必须用递归来解决它:

getAll :: (Int, Int) -> [(Int,Int)]
getAll (x, y) = go 0 0 where
  go x' y'
    | x' == x            = []
    | y' == y            = go (x'+1) 0
    | otherwise          = (x', y') : go x' (y'+1)

【讨论】:

    【解决方案2】:

    你写

    getAll (x,y) 
      | x == 0 && y == 0 = [(0,0)] 
    

    到目前为止一切顺利。

      | x == 1 && y == 1 = [(1,1)] 
    

    这已经是错误的了。它应该产生[(0,0), (0,1), (1,0), (1,1)]

    从描述中可以清楚地看到。两个Ints 定义了两个范围,

    [ (i,j) | i <- [0..x-1], j <- [0..y-1]]
    

    这称为列表推导。事实上,这段代码就是你在函数中所需要的。也不需要测试任何情况。它会起作用的。


    但这意味着什么?就像看起来一样,在视觉上,它创建了一个列表,列出了从 0xjs 从 0y 的所有 is 配对。

    还有很多其他的编码方式,但归根结底,它是关于枚举矩形中的单元格

           j   0  1  2  ........   y-1
        i
         0     *  *  *  *  *  *  *  *
         1     *  *  *  *  *  *  *  *
         2     *  *  *  *  *  *  *  *
         .     *  *  *  *  *  *  *  *
         .     *  *  *  *  *  *  *  *
        x-1    *  *  *  *  *  *  *  *
    

    可以用伪代码表示为两个嵌套循环:

        for  i  in  0 .. x-1 :
           for  j  in  0 .. y-1 :
               produce  (i,j)
    

    嵌套循环可以展开成一系列循环:

        for  i=0  and  j  in  0 .. y-1 : produce  (i,j)
        for  i=1  and  j  in  0 .. y-1 : produce  (i,j)
        for  i=2  and  j  in  0 .. y-1 : produce  (i,j)
        ..............
        for  i=x-1 and j  in  0 .. y-1 : produce  (i,j)
    

    上面可以表示为一个循环,它在达到限制后重置j 值,同时推进i 值......这是其他答案中的递归版本正在做的事情,一对一地产生对并使用: 将它们放入输出列表中。嵌套循环通常在 Haskell 中编码为concatMaps,逐一(逐行)生成列表,并将它们与++ 组合,而不是:.... p>

    有很多方法可以对此进行编码。但最终它是关于追踪那个正方形(矩形,随便):

        concat                              -- concat 
            [ [ (i , j)                     --   [ map (i ,)  
                   | j <- [0..y-1] ]        --               [0..y-1]
               | i <- [0..x-1] ]            --       |  i <-  [0..x-1] ]
    ==
           [ (0,j) | j <- [0..y-1] ]  ++
           [ (1,j) | j <- [0..y-1] ]  ++
           [ (2,j) | j <- [0..y-1] ]  ++
           ............
          [ (x-1,j) | j <- [0..y-1] ] 
    

    你可以看到这一切都是一样的。

    我们甚至可以通过 对角线来追踪它。

    【讨论】:

    • 谢谢!我没有学习列表推导,但我会尝试将其转换为递归!
    • 哇,伙计!您为此投入的精力和细节令人惊叹!尊重!
    【解决方案3】:

    您的递归函数最终应该终止,但您仅在(0, 0)(1, 1) 的情况下终止。如果xy 大于1,那么您进行递归调用,同时增加xy,因此这永远不会终止。其他情况会被忽略,因此 (0, 1) 会引发错误。

    您可以使用以(0, 0) 开头的辅助函数,每次递增y 坐标,直到该坐标大于或等于上限。在这种情况下,我们使用 y 坐标零进行递归调用,并增加 x 坐标。喜欢:

    getAll :: (Int, Int) -> [(Int, Int)]
    getAll (xn, yn) = go (0, 0)
        where go (x, y)
                | x >= xn = []  -- terminate, we reached the end
                | y >= yn = go (x + 1, 0)  -- increment x, set y to 0
                | otherwise = (x, y) : go (x, y + 1) -- yield and increment y

    由于项目可以按任何顺序返回,另一种策略是递减值,从而将y(或x)设置回初始y(或x)并递减另一个坐标(yx)直到两者都为零,所以:

    getAll :: (Int, Int) -> [(Int, Int)]
    getAll t@(_, yn) = go t
        where go (x, y) 
                | x < 0 = []
                | y < 0 = go (x-1, yn)
                | otherwise = (x, y) : go (x, y-1)

    但是,您可以使用列表推导式或列表的 Applicative 实例以更紧凑的方式生成此结果。例如:

    getAll :: (Int, Int) -> [(Int, Int)]
    getAll (x, y) = (,) <$> [0 .. x-1] <*> [0 .. y-1]
    

    这里f &lt;$&gt; xs &lt;*&gt; ys 将构造一个列表,我们在其中为xsys 中的每个项目组合调用f。因此,我们将对来自xs 的元素x<sub>i</sub> 和来自ys 的每个元素y<sub>i</sub> 的每个组合调用(,) x<sub>i</sub> y<sub>i</sub>,这是(x<sub>i</sub>, y<sub>i</sub>) 的更规范形式

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-11-12
      • 1970-01-01
      • 2021-05-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多