简体   繁体   English

如何在找到正确的三角形时使用Haskells懒惰

[英]How to use Haskells laziness when finding right triangles

I'm following the (excellent) Haskell tutorial at http://learnyouahaskell.com/starting-out and am trying out the right triangle example: 我正在http://learnyouahaskell.com/starting-out上关注(优秀的)Haskell教程,并尝试使用正确的三角形示例:

> let triangles = [(a,b,c) | c <- [1..10], b <- [1..10], a <- [1..10], a^2 + b^2 == c^2]

running this I get, as expected: 按照预期运行我得到的:

> triangles 
[(4,3,5),(3,4,5),(8,6,10),(6,8,10)]

Now, I'd like to try using infinite lists instead: 现在,我想尝试使用无限列表:

> let triangles = [(a,b,c) | c <- [1..], b <- [1..], a <- [1..], a^2 + b^2 == c^2] 

But when I try it, like: 但是,当我尝试它时,像:

> take 2 triangles

...the programs just runs and runs with no output. ...程序只运行并运行,没有输出。 What am I doing wrong? 我究竟做错了什么? I thought Haskells laziness would cause it to find the two first triangles and then halt? 我认为Haskells懒惰会导致它找到两个第一个三角形然后停止?

Well, the laziness isn't the problem here. 嗯,懒惰不是问题。 It's the order in which you're iterating the variables in the list. 这是您在列表中迭代变量的顺序。

Basically what happens is: 基本上会发生什么:

  1. c is bound to 1 c绑定为1
  2. b is bound to 1 b必然是1
  3. a is bound to 1 a必然是1
  4. Equation is checked 检查方程式
  5. a is bound to 2 a必然是2
  6. Equation is checked 检查方程式
  7. a is bound to 3 a必然是3
  8. Equation is checked 检查方程式

and it goes on forever. 它会永远持续下去。

So the generator keeps on iterating and binding values for a , because it doesn't know that you need to stop and also increment b or c for a change. 因此,发电机不断迭代和对绑定值的a ,因为它不知道你需要停下来,也增加bc一变。

So you need to generate tuples in more balanced ways. 所以你需要以更平衡的方式生成元组。

You can use, for instance, this method: 例如,您可以使用此方法:

triplesN :: Int -> [(Int, Int, Int)]
triplesN n = [(i, j, n - i - j) | i <- [1..n - 2], 
                                  j <- [1..n - i - 1], i>=j, 
                                  let k = n - i - j,   j>=k]

isTriangle (a, b, c) = a^2 == b^2 + c^2

triangles = filter isTriangle $ concatMap triplesN [1..]

tripleN generates all the ordered triples with sum n . tripleN生成所有有序三元组,其总和为n By mapping this function over all the natural numbers we actually get the stream of all ordered pairs. 通过在所有自然数上映射此函数,我们实际上获得了所有有序对的流。 And finally, we filter only those triples that are triangles. 最后,我们只过滤那些三角形的三元组。

By doing: 通过做:

take 10 triangles

we get: 我们得到:

[(5,4,3),(10,8,6),(13,12,5),(15,12,9),(17,15,8),(20,16,12),(25,24,7),(25,20,15),(26,24,10),(29,21,20)]

You may be interested in reading the article A Monad for Combinatorial Search on sigfpe's blog. 您可能有兴趣阅读sigfpe博客上的文章A Monad for Combinatorial Search

He defines a new monad called a Penalty List or PList , similar to the list monad, but which also has the concept of a penalty for more complex solutions. 他定义了一个名为惩罚列表PList的新monad,类似于列表monad,但它也有更复杂解决方案的惩罚概念。 When you combine PLists, the order that the results are generated is the order smallest penalty --> largest penalty. 当您组合PLists时,生成结果的顺序是最小罚分 - >最大罚分。

In your example, the penalty associated with an integer could be equal to the size of the integer, and the penalty associated with a tuple is the sum of the penalties of its elements. 在您的示例中,与整数关联的惩罚可能等于整数的大小,与元组关联的惩罚是其元素的惩罚之和。 So the tuple (3,4,5) has penalty 3+4+5 = 12, and the tuple (5,12,13) has penalty 5+12+13 = 30. 因此元组(3,4,5)惩罚为3 + 4 + 5 = 12,而元组(5,12,13)惩罚为5 + 12 + 13 = 30。

With the list monad, the order of the tuples produced is 使用列表monad,生成的元组的顺序是

(1,1,1), (1,1,2), (1,1,3), (1,1,4), (1,1,5) ...

and you never see a tuple not of the form (1,1,x) . 你永远不会看到一个不是形式(1,1,x)的元组。 With the PList monad, the tuples produced might be 使用PList monad,产生的元组可能是

(1,1,1), (1,1,2), (1,2,1), (2,1,1), (1,1,3), (1,3,1), (3,1,1), (1,2,2) ...

with all 'smaller' tuples generated before 'larger' ones. 在'较大'之前生成所有'较小'元组。

For your particular problem this solution is overkill, but it can be very useful in more complex problems. 对于您的特定问题,此解决方案过度,但在更复杂的问题中它非常有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM