理解一点Haskell

时间:2012-06-08 14:02:59

标签: haskell set combinatorics predicate

我有一个关于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]

令我困惑的部分是ba绑定的上限扩展。根据我收集的内容,..c..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值之外,显然代表同一个三角形。

那么,有人可以引导我完成逻辑/执行/评估这里发生的事情吗?

3 个答案:

答案 0 :(得分: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

答案 1 :(得分:1)

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

[(8,6,10)]

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

答案 2 :(得分: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贬低你的理解相似。从中可以明显看出每个变量abc的范围。

或者,可以使用List monad:

编写
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)

考虑到List Monad和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