2

我有一个关于 Haskell 的快速问题。我一直在关注Learn You a Haskell,并且对以下代码段的执行顺序/逻辑有点困惑,用于计算三角形的边长,当所有边都等于或小于 10 并且三角形的总周长为 24:

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

令我困惑的部分是banda绑定的上扩展界限。根据我的收集,..cand..b用于删除同一组三角形边的其他排列(组合?)。

当我用 运行它时..c/b,我得到了答案:

[(6,8,10)]

当我没有..c/b

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

正如我最初输入时没有那样,我得到:

[(8,6,10),(6,8,10)]

这显然代表同一个三角形,除了ab值已交换。

那么,有人可以带我了解这里发生的事情的逻辑/执行/评估吗?

4

3 回答 3

7

原始版本考虑所有三元组 (a,b,c),其中 c 是 1 和 10 之间的数字,b 是 1 和 c 之间的数字,a 是 1 和 b 之间的数字。(6,8,10) 符合该标准,(8,6,10) 不符合(因为这里 a 是 8,b 是 6,所以 a 不在 0 和 6 之间)。

在您的版本中,您考虑所有三元组 (a,b,c),其中 a、b 和 c 介于 1 和 10 之间。您对 a、b 和 c 如何相互关联没有任何限制,因此 (8, 6, 10)不排除,因为其中的所有数字确实在 1 到 10 之间。

如果您从命令式 for 循环的角度来考虑它,您的版本会这样做:

for c from 1 to 10:
  for b from 1 to 10:
    for a from 1 to 10:
      if a^2 + b^2 == c^2 and a+b+c==24:
        add (a,b,c) to the result

而原始版本这样做:

for c from 1 to 10:
  for b from 1 to c:
    for c from 1 to b:
      if a^2 + b^2 == c^2 and a+b+c==24:
        add (a,b,c) to the result
于 2012-06-08T14:12:45.513 回答
1

这与执行顺序无关。在第一个示例中,您看不到退化的解决方案

[(8,6,10)]

因为a <= b <= c. 在第二种情况下a > b,包含在列表理解中。

于 2012-06-08T14:11:18.320 回答
1

列表推导可以用其他函数来编写,例如concatMap,它阐明了绑定的范围。作为一个单行,你的例子变成了这样:

concatMap (\c -> concatMap (\b -> concatMap (\a -> if a^2 + b^2 == c^2 then (if a+b+c == 24 then [(a,b,c)] else []) else []) (enumFromTo 1 b)) (enumFromTo 1 c)) (enumFromTo 1 10)

是的,这看起来很丑陋,但它类似于 Haskell 将你的理解分解成的内容。每个变量的范围a,bc, 应该是显而易见的。

或者,这可以用Listmonad 编写:

import Control.Monad

example = do c <- [1..10]
             b <- [1..c]
             a <- [1..b]
             guard (a^2 + b^2 == c^2)
             guard (a+b+c == 24)
             return (a,b,c)

This is actually very similar as the one-liner above, given the definition of the List Monad and guard:

instance Monad [] where
    return x = [x]
    xs >>= f = concatMap f xs

instance MonadPlus [] where
    mzero = []
    mconcat = (++)

guard bool = if bool then return () else mzero
于 2012-06-08T18:00:14.573 回答