我有一个关于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]
令我困惑的部分是b
和a
绑定的上限扩展。根据我收集的内容,..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)]
除了已交换a
和b
值之外,显然代表同一个三角形。
那么,有人可以引导我完成逻辑/执行/评估这里发生的事情吗?
答案 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贬低你的理解相似。从中可以明显看出每个变量a
,b
和c
的范围。
或者,可以使用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