首页 > 解决方案 > 如何在没有映射或列表理解的情况下在 Haskell 中编写简单的递归双循环

问题描述

我试图理解递归,所以我想我会用原始的 Haskell 编写一个简单的双 for 循环(没有列表推导,没有映射等)。

给定 i 和 j 产生以下..

rTest01 2 5

[[2,5],[2,4],[2,3],[2,2],[2,1],[1,5],[1,4],[1,3],[1,2],[1,1]]

我的代码有效,但看起来并不漂亮.. 每次“i”更改时,我都必须添加一个额外的参数来重新加载“j”。

有没有更简洁的写法?

rTest01 :: Int -> Int -> [[Int]]
rTest01 i j =
      [[i,j]] ++ rTest01' j i (j-1)

rTest01' :: Int -> Int -> Int -> [[Int]]
rTest01' origJ i 0 = rTest01' origJ (i-1) origJ
rTest01' _ 0 _ = []
rTest01' origJ i j  =
    [[i,j]] ++ rTest01' origJ i (j-1)

标签: haskellrecursion

解决方案


这是内部递归的一个很好的用例。就像在 Python 或 Java 中,我们可能有一个私有递归函数,它只被一个公共非递归函数调用(后者不接受额外的“帮助”参数),我们可以做同样的事情在 Haskell 中使用嵌套范围。

example :: Int -> Int -> [(Int, Int)]
example iorig jorig = go iorig jorig
    where go 0 _ = []
          go i 0 = go (i - 1) jorig
          go i j = (i, j) : go i (j - 1)

虽然我非常感谢您的递归练习,并且我确实强调编写这样的代码是一个非常好的练习,因为它练习了更复杂的 Haskell 编码所需的许多基本技能,请记住这一点,即使按照我的方式编写上面,这不是惯用的 Haskell。如果我在生产中编写这个函数,我会写

example :: Int -> Int -> [(Int, Int)]
example i j = reverse $ (,) <$> [1..i] <*> [1..j]

(删除reverse如果我们不关心订单)

递归是一种非常方便的技能,但它并不总是解决方案。事实上,在 Haskell 中,我们通常可以构建其他几个抽象来使我们的代码更简单,例如上面示例中的应用程序和 monad。

祝你好运,编码愉快!:)


推荐阅读